diff options
author | Ton Roosendaal <ton@blender.org> | 2006-01-11 01:10:14 +0300 |
---|---|---|
committer | Ton Roosendaal <ton@blender.org> | 2006-01-11 01:10:14 +0300 |
commit | e7285229b824f959f84efe6774c506034cf0f98e (patch) | |
tree | f4c96dae29629cc5e5b1c335d93abc82647a11c5 /release | |
parent | d594594cbe9c9eb3bc3c8a7708601e68693d324d (diff) | |
parent | 185c6bb49ce994d66fc67673b01a014161fa307d (diff) |
Tuesday merger of bf-blender into orange branch.
Diffstat (limited to 'release')
-rw-r--r-- | release/scripts/3ds_export.py | 673 | ||||
-rw-r--r-- | release/scripts/3ds_import.py | 662 | ||||
-rw-r--r-- | release/scripts/blenderLipSynchro.py | 602 | ||||
-rwxr-xr-x | release/scripts/flt_export.py | 723 | ||||
-rw-r--r-- | release/scripts/flt_filewalker.py | 278 | ||||
-rwxr-xr-x | release/scripts/flt_import.py | 1803 | ||||
-rw-r--r-- | release/scripts/md2_export.py | 1016 | ||||
-rw-r--r-- | release/scripts/md2_import.py | 571 | ||||
-rw-r--r-- | release/scripts/mirror_bone_weights.py | 218 | ||||
-rw-r--r-- | release/scripts/widgetwizard.py | 913 | ||||
-rw-r--r-- | release/scripts/xsi_export.py | 1245 |
11 files changed, 8704 insertions, 0 deletions
diff --git a/release/scripts/3ds_export.py b/release/scripts/3ds_export.py new file mode 100644 index 00000000000..ebe06c3d4eb --- /dev/null +++ b/release/scripts/3ds_export.py @@ -0,0 +1,673 @@ +#!BPY + +""" +Name: '3D Studio (.3ds)...' +Blender: 237 +Group: 'Export' +Tooltip: 'Export to 3DS file format (.3ds).' +""" + +__author__ = ["Campbell Barton", "Bob Holcomb", "Richard Lärkäng", "Damien McGinnes"] +__url__ = ("blender", "elysiun", "http://www.gametutorials.com") +__version__ = "0.82" +__bpydoc__ = """\ + +3ds Exporter + +This script Exports a 3ds file and the materials into blender for editing. + +Exporting is based on 3ds loader from www.gametutorials.com(Thanks DigiBen). +""" + +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# Script copyright (C) Bob Holcomb +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# ***** END GPL LICENCE BLOCK ***** +# -------------------------------------------------------------------------- + + +###################################################### +# Importing modules +###################################################### + +import Blender +from Blender import NMesh, Scene, Object, Material +import struct + + +###################################################### +# Data Structures +###################################################### + +#Some of the chunks that we will export +#----- Primary Chunk, at the beginning of each file +PRIMARY= long("0x4D4D",16) + +#------ Main Chunks +OBJECTINFO = long("0x3D3D",16); #This gives the version of the mesh and is found right before the material and object information +VERSION = long("0x0002",16); #This gives the version of the .3ds file +EDITKEYFRAME= long("0xB000",16); #This is the header for all of the key frame info + +#------ sub defines of OBJECTINFO +MATERIAL=45055 #0xAFFF // This stored the texture info +OBJECT=16384 #0x4000 // This stores the faces, vertices, etc... + +#>------ sub defines of MATERIAL +MATNAME = long("0xA000",16); # This holds the material name +MATAMBIENT = long("0xA010",16); # Ambient color of the object/material +MATDIFFUSE = long("0xA020",16); # This holds the color of the object/material +MATSPECULAR = long("0xA030",16); # SPecular color of the object/material +MATSHINESS = long("0xA040",16); # ?? +MATMAP = long("0xA200",16); # This is a header for a new material +MATMAPFILE = long("0xA300",16); # This holds the file name of the texture + +RGB1= long("0x0011",16) +RGB2= long("0x0012",16) + +#>------ sub defines of OBJECT +OBJECT_MESH = long("0x4100",16); # This lets us know that we are reading a new object +OBJECT_LIGHT = long("0x4600",16); # This lets un know we are reading a light object +OBJECT_CAMERA= long("0x4700",16); # This lets un know we are reading a camera object + +#>------ sub defines of CAMERA +OBJECT_CAM_RANGES= long("0x4720",16); # The camera range values + +#>------ sub defines of OBJECT_MESH +OBJECT_VERTICES = long("0x4110",16); # The objects vertices +OBJECT_FACES = long("0x4120",16); # The objects faces +OBJECT_MATERIAL = long("0x4130",16); # This is found if the object has a material, either texture map or color +OBJECT_UV = long("0x4140",16); # The UV texture coordinates +OBJECT_TRANS_MATRIX = long("0x4160",16); # The Object Matrix + +#==============================================# +# Strips the slashes from the back of a string # +#==============================================# +def stripPath(path): + return path.split('/')[-1].split('\\')[-1] + +#==================================================# +# New name based on old with a different extension # +#==================================================# +def newFName(ext): + return Blender.Get('filename')[: -len(Blender.Get('filename').split('.', -1)[-1]) ] + ext + + +#the chunk class +class chunk: + ID=0 + size=0 + + def __init__(self): + self.ID=0 + self.size=0 + + def get_size(self): + self.size=6 + + def write(self, file): + #write header + data=struct.pack(\ + "<HI",\ + self.ID,\ + self.size) + file.write(data) + + def dump(self): + print "ID: ", self.ID + print "ID in hex: ", hex(self.ID) + print "size: ", self.size + + + +#may want to add light, camera, keyframe chunks. +class vert_chunk(chunk): + verts=[] + + def __init__(self): + self.verts=[] + self.ID=OBJECT_VERTICES + + def get_size(self): + chunk.get_size(self) + temp_size=2 #for the number of verts short + temp_size += 12 * len(self.verts) #3 floats x 4 bytes each + self.size+=temp_size + #~ print "vert_chunk size: ", self.size + return self.size + + def write(self, file): + chunk.write(self, file) + #write header + data=struct.pack("<H", len(self.verts)) + file.write(data) + #write verts + for vert in self.verts: + data=struct.pack("<3f",vert[0],vert[1], vert[2]) + file.write(data) + +class obj_material_chunk(chunk): + name="" + faces=[] + + def __init__(self): + self.name="" + self.faces=[] + self.ID=OBJECT_MATERIAL + + def get_size(self): + chunk.get_size(self) + temp_size=(len(self.name)+1) + temp_size+=2 + for face in self.faces: + temp_size+=2 + self.size+=temp_size + #~ print "obj material chunk size: ", self.size + return self.size + + def write(self, file): + chunk.write(self, file) + #write name + name_length=len(self.name)+1 + binary_format="<"+str(name_length)+"s" + data=struct.pack(binary_format, self.name) + file.write(data) + binary_format="<H" + #~ print "Nr of faces: ", len(self.faces) + data=struct.pack(binary_format, len(self.faces)) + file.write(data) + for face in self.faces: + data=struct.pack(binary_format, face) + file.write(data) + +class face_chunk(chunk): + faces=[] + num_faces=0 + m_chunks=[] + + def __init__(self): + self.faces=[] + self.ID=OBJECT_FACES + self.num_faces=0 + self.m_chunks=[] + + def get_size(self): + chunk.get_size(self) + temp_size = 2 #num faces info + temp_size += 8 * len(self.faces) #4 short ints x 2 bytes each + for m in self.m_chunks: + temp_size+=m.get_size() + self.size += temp_size + #~ print "face_chunk size: ", self.size + return self.size + + def write(self, file): + chunk.write(self, file) + data=struct.pack("<H", len(self.faces)) + file.write(data) + #write faces + for face in self.faces: + data=struct.pack("<4H", face[0],face[1], face[2], 0) # The last zero is only used by 3d studio + file.write(data) + #write materials + for m in self.m_chunks: + m.write(file) + +class uv_chunk(chunk): + uv=[] + num_uv=0 + + def __init__(self): + self.uv=[] + self.ID=OBJECT_UV + self.num_uv=0 + + def get_size(self): + chunk.get_size(self) + temp_size=2 #for num UV + for this_uv in self.uv: + temp_size+=8 #2 floats at 4 bytes each + self.size+=temp_size + #~ print "uv chunk size: ", self.size + return self.size + + def write(self, file): + chunk.write(self, file) + + #write header + data=struct.pack("<H", len(self.uv)) + file.write(data) + + #write verts + for this_uv in self.uv: + data=struct.pack("<2f", this_uv[0], this_uv[1]) + file.write(data) + +class mesh_chunk(chunk): + v_chunk=vert_chunk() + f_chunk=face_chunk() + uv_chunk=uv_chunk() + + def __init__(self): + self.v_chunk=vert_chunk() + self.f_chunk=face_chunk() + self.uv_chunk=uv_chunk() + self.ID=OBJECT_MESH + + def get_size(self): + chunk.get_size(self) + temp_size=self.v_chunk.get_size() + temp_size+=self.f_chunk.get_size() + temp_size+=self.uv_chunk.get_size() + self.size+=temp_size + #~ print "object mesh chunk size: ", self.size + return self.size + + def write(self, file): + chunk.write(self, file) + #write stuff + self.v_chunk.write(file) + self.f_chunk.write(file) + self.uv_chunk.write(file) + +class object_chunk(chunk): + name="" + mesh_chunks=[] + + def __init__(self): + self.name="" + self.mesh_chunks=[] + self.ID=OBJECT + + def get_size(self): + chunk.get_size(self) + temp_size=len(self.name)+1 #+1 for null character + for mesh in self.mesh_chunks: + temp_size+=mesh.get_size() + self.size+=temp_size + #~ print "object chunk size: ", self.size + return self.size + + def write(self, file): + chunk.write(self, file) + #write name + + binary_format = "<%ds" % (len(self.name)+1) + data=struct.pack(binary_format, self.name) + file.write(data) + #write stuff + for mesh in self.mesh_chunks: + mesh.write(file) + +class object_info_chunk(chunk): + obj_chunks=[] + mat_chunks=[] + + def __init__(self): + self.obj_chunks=[] + self.mat_chunks=[] + self.ID=OBJECTINFO + + def get_size(self): + chunk.get_size(self) + temp_size=0 + for mat in self.mat_chunks: + temp_size+=mat.get_size() + for obj in self.obj_chunks: + temp_size+=obj.get_size() + self.size+=temp_size + #~ print "object info size: ", self.size + return self.size + + def write(self, file): + chunk.write(self, file) + #write all the materials + for mat in self.mat_chunks: + mat.write(file) + #write all the objects + for obj in self.obj_chunks: + obj.write(file) + + + +class version_chunk(chunk): + version=3 + + def __init__(self): + self.ID=VERSION + self.version=3 #that the document that I'm using + + def get_size(self): + chunk.get_size(self) + self.size += 4 #bytes for the version info + #~ print "version chunk size: ", self.size + return self.size + + def write(self, file): + chunk.write(self, file) + #write header and version + data=struct.pack("<I", self.version) + file.write(data) + +class rgb_chunk(chunk): + col=[] + + def __init__(self): + self.col=[] + + def get_size(self): + chunk.get_size(self) + self.size+=3 #color size + #~ print "rgb chunk size: ", self.size + return self.size + + def write(self, file): + chunk.write(self, file) + #write colors + for c in self.col: + file.write( struct.pack("<c", chr(int(255*c))) ) + + +class rgb1_chunk(rgb_chunk): + + def __init__(self): + self.ID=RGB1 + +class rgb2_chunk(rgb_chunk): + + def __init__(self): + self.ID=RGB2 + +class material_ambient_chunk(chunk): + col1=None + col2=None + + def __init__(self): + self.ID=MATAMBIENT + self.col1=rgb1_chunk() + self.col2=rgb2_chunk() + + def get_size(self): + chunk.get_size(self) + temp_size=self.col1.get_size() + temp_size+=self.col2.get_size() + self.size+=temp_size + #~ print "material ambient size: ", self.size + return self.size + + def write(self, file): + chunk.write(self, file) + #write colors + self.col1.write(file) + self.col2.write(file) + +class material_diffuse_chunk(chunk): + col1=None + col2=None + + def __init__(self): + self.ID=MATDIFFUSE + self.col1=rgb1_chunk() + self.col2=rgb2_chunk() + + def get_size(self): + chunk.get_size(self) + temp_size=self.col1.get_size() + temp_size+=self.col2.get_size() + self.size+=temp_size + #~ print "material diffuse size: ", self.size + return self.size + + def write(self, file): + chunk.write(self, file) + #write colors + self.col1.write(file) + self.col2.write(file) + +class material_specular_chunk(chunk): + col1=None + col2=None + + def __init__(self): + self.ID=MATSPECULAR + self.col1=rgb1_chunk() + self.col2=rgb2_chunk() + + def get_size(self): + chunk.get_size(self) + temp_size=self.col1.get_size() + temp_size+=self.col2.get_size() + self.size+=temp_size + #~ print "material specular size: ", self.size + return self.size + + def write(self, file): + chunk.write(self, file) + #write colors + self.col1.write(file) + self.col2.write(file) + +class material_name_chunk(chunk): + name="" + + def __init__(self): + self.ID=MATNAME + self.name="" + + def get_size(self): + chunk.get_size(self) + temp_size=(len(self.name)+1) + self.size+=temp_size + #~ print "material name size: ", self.size + return self.size + + def write(self, file): + chunk.write(self, file) + #write name + name_length=len(self.name)+1 + binary_format="<"+str(name_length)+"s" + data=struct.pack(binary_format, self.name) + file.write(data) + +class material_chunk(chunk): + matname_chunk=None + matambient_chunk=None + matdiffuse_chunk=None + matspecular_chunk=None + + def __init__(self): + self.ID=MATERIAL + self.matname_chunk=material_name_chunk() + self.matambient_chunk=material_ambient_chunk() + self.matdiffuse_chunk=material_diffuse_chunk() + self.matspecular_chunk=material_specular_chunk() + + def get_size(self): + chunk.get_size(self) + temp_size=self.matname_chunk.get_size() + temp_size+=self.matambient_chunk.get_size() + temp_size+=self.matdiffuse_chunk.get_size() + temp_size+=self.matspecular_chunk.get_size() + self.size+=temp_size + #~ print "material chunk size: ", self.size + return self.size + + def write(self, file): + chunk.write(self, file) + #write name chunk + self.matname_chunk.write(file) + #write material colors + self.matambient_chunk.write(file) + self.matdiffuse_chunk.write(file) + self.matspecular_chunk.write(file) + +class primary_chunk(chunk): + version=None + obj_info=None + + def __init__(self): + self.version=version_chunk() + self.obj_info=object_info_chunk() + self.ID=PRIMARY + + def get_size(self): + chunk.get_size(self) + temp_size=self.version.get_size() + temp_size+=self.obj_info.get_size() + self.size+=temp_size + #~ print "primary chunk size: ", self.size + return self.size + + def write(self, file): + chunk.write(self, file) + #write version chunk + self.version.write(file) + #write object_info chunk + self.obj_info.write(file) + +def read_chunk(file, chunk): + chunk.ID, chunk.size = \ + struct.unpack(\ + chunk.binary_format, \ + file.read(struct.calcsize(chunk.binary_format)) ) + +def read_string(file): + s="" + index=0 + + #read in the characters till we get a null character + data=struct.unpack("c", file.read(struct.calcsize("c"))) + s=s+(data[0]) + #print "string: ",s + while(ord(s[index])!=0): + index+=1 + data=struct.unpack("c", file.read(struct.calcsize("c"))) + s=s+(data[0]) + #print "string: ",s + return str(s) + +###################################################### +# EXPORT +###################################################### +def save_3ds(filename): + # Time the export + time1 = Blender.sys.time() + + exported_materials = {} + + #fill the chunks full of data + primary=primary_chunk() + #get all the objects in this scene + object_list = [ ob for ob in Blender.Object.GetSelected() if ob.getType() == 'Mesh' ] + #fill up the data structures with objects + for obj in object_list: + #create a new object chunk + primary.obj_info.obj_chunks.append(object_chunk()) + #get the mesh data + blender_mesh = obj.getData() + blender_mesh.transform(obj.getMatrix()) + #set the object name + primary.obj_info.obj_chunks[len(primary.obj_info.obj_chunks)-1].name=obj.getName() + + matrix = obj.getMatrix() + + #make a new mesh chunk object + mesh=mesh_chunk() + + mesh.v_chunk.verts = blender_mesh.verts + + dummy = None # just incase... + + for m in blender_mesh.materials: + mesh.f_chunk.m_chunks.append(obj_material_chunk()) + mesh.f_chunk.m_chunks[len(mesh.f_chunk.m_chunks)-1].name = m.name + + # materials should only be exported once + try: + dummy = exported_materials[m.name] + + + except KeyError: + material = material_chunk() + material.matname_chunk.name=m.name + material.matambient_chunk.col1.col = m.mirCol + material.matambient_chunk.col2.col = m.mirCol + material.matdiffuse_chunk.col1.col = m.rgbCol + material.matdiffuse_chunk.col2.col = m.rgbCol + material.matspecular_chunk.col1.col = m.specCol + material.matspecular_chunk.col2.col = m.specCol + + primary.obj_info.mat_chunks.append(material) + + exported_materials[m.name] = None + + del dummy # unpolute the namespace + + valid_faces = [f for f in blender_mesh.faces if len(f) > 2] + facenr=0 + #fill in faces + for face in valid_faces: + + #is this a tri or a quad + num_fv=len(face.v) + + + #it's a tri + if num_fv==3: + mesh.f_chunk.faces.append((face[0].index, face[1].index, face[2].index)) + if (face.materialIndex < len(mesh.f_chunk.m_chunks)): + mesh.f_chunk.m_chunks[face.materialIndex].faces.append(facenr) + facenr+=1 + + else: #it's a quad + mesh.f_chunk.faces.append((face[0].index, face[1].index, face[2].index)) # 0,1,2 + mesh.f_chunk.faces.append((face[2].index, face[3].index, face[0].index)) # 2,3,0 + #first tri + if (face.materialIndex < len(mesh.f_chunk.m_chunks)): + mesh.f_chunk.m_chunks[face.materialIndex].faces.append(facenr) + facenr+=1 + #other tri + if (face.materialIndex < len(mesh.f_chunk.m_chunks)): + mesh.f_chunk.m_chunks[face.materialIndex].faces.append(facenr) + facenr+=1 + + + #fill in the UV info + if blender_mesh.hasVertexUV(): + for vert in blender_mesh.verts: + mesh.uv_chunk.uv.append((vert.uvco[0], vert.uvco[1])) + + elif blender_mesh.hasFaceUV(): + for face in valid_faces: + # Tri or quad. + for uv_coord in face.uv: + mesh.uv_chunk.uv.append((uv_coord[0], uv_coord[1])) + + #filled in our mesh, lets add it to the file + primary.obj_info.obj_chunks[len(primary.obj_info.obj_chunks)-1].mesh_chunks.append(mesh) + + #check the size + primary.get_size() + #open the files up for writing + file = open( filename, "wb" ) + #recursively write the stuff to file + primary.write(file) + file.close() + print "3ds export time: %.2f" % (Blender.sys.time() - time1) + + +Blender.Window.FileSelector(save_3ds, "Export 3DS", newFName('3ds')) diff --git a/release/scripts/3ds_import.py b/release/scripts/3ds_import.py new file mode 100644 index 00000000000..017ec74123f --- /dev/null +++ b/release/scripts/3ds_import.py @@ -0,0 +1,662 @@ +#!BPY + +""" +Name: '3D Studio (.3ds)...' +Blender: 237 +Group: 'Import' +Tooltip: 'Import from 3DS file format. (.3ds)' +""" + +__author__ = ["Bob Holcomb", "Richard Lärkäng", "Damien McGinnes", "Campbell Barton"] +__url__ = ("blender", "elysiun", "http://www.gametutorials.com") +__version__ = "0.92" +__bpydoc__ = """\ + +3ds Importer + +This script imports a 3ds file and the materials into Blender for editing. + +Loader is based on 3ds loader from www.gametutorials.com (Thanks DigiBen). + +Changes: + +0.92<br> +- Added support for diffuse, alpha, spec, bump maps in a single material + +0.9<br> +- Reorganized code into object/material block functions<br> +- Use of Matrix() to copy matrix data<br> +- added support for material transparency<br> + +0.81a (fork- not 0.9) Campbell Barton 2005-06-08<br> +- Simplified import code<br> +- Never overwrite data<br> +- Faster list handling<br> +- Leaves import selected<br> + +0.81 Damien McGinnes 2005-01-09<br> +- handle missing images better<br> + +0.8 Damien McGinnes 2005-01-08<br> +- copies sticky UV coords to face ones<br> +- handles images better<br> +- Recommend that you run 'RemoveDoubles' on each imported mesh after using this script + +""" + +# $Id$ +# +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# Script copyright (C) Bob Holcomb +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# ***** END GPL LICENCE BLOCK ***** +# -------------------------------------------------------------------------- + + +# Importing modules + +import Blender +from Blender import NMesh, Scene, Object, Material, Image, Texture + +import sys, struct, string + +import os + +###################################################### +# Data Structures +###################################################### +#----- Primary Chunk, +PRIMARY = long("0x4D4D",16) # should be aat the beginning of each file +VERSION = long("0x0002",16) #This gives the version of the .3ds file +EDITOR_BLOCK = long("0x3D3D",16) #this is the Editor Data block, contains objects, materials +KEYFRAME_BLOCK = long("0xB000",16) #This is the header for all of the key frame info + +#------ sub defines of EDITOR_BLOCK +MATERIAL_BLOCK = long("0xAFFF",16) #This stores the Material info +OBJECT_BLOCK = long("0x4000",16) #This stores the Object,Camera,Light + +#------ sub defines of OBJECT_BLOCK +OBJECT_MESH = long("0x4100",16) # This lets us know that we are reading a new object +OBJECT_LIGHT = long("0x4600",16) # This lets un know we are reading a light object +OBJECT_CAMERA = long("0x4700",16) # This lets un know we are reading a camera object + +#------ sub defines of OBJECT_MESH +MESH_VERTICES = long("0x4110",16) # The objects vertices +MESH_FACES = long("0x4120",16) # The objects faces +MESH_MATERIAL = long("0x4130",16) # This is found if the object has a material, either texture map or color +MESH_UV = long("0x4140",16) # The UV texture coordinates +MESH_TRANS_MATRIX = long("0x4160",16) # The Object Matrix +MESH_COLOR = long("0x4165",16) # The color of the object +MESH_TEXTURE_INFO = long("0x470",16) # Info about the Object Texture + +#------ sub defines of OBJECT_CAMERA +CAMERA_CONE = long("0x4710",16) # The camera see cone +CAMERA_RANGES = long("0x4720",16) # The camera range values + +#------ sub defines of OBJECT_LIGHT +LIGHT_SPOTLIGHT = long("0x4610",16) # A spotlight +LIGHT_ATTENUATE = long("0x4625",16) # Light attenuation values + + +#------ sub defines of MATERIAL_BLOCK +MAT_NAME = long("0xA000",16) # This holds the material name +MAT_AMBIENT = long("0xA010",16) # Ambient color of the object/material +MAT_DIFFUSE = long("0xA020",16) # This holds the color of the object/material +MAT_SPECULAR = long("0xA030",16) # SPecular color of the object/material +MAT_SHINESS = long("0xA040",16) # ?? +MAT_TRANSPARENCY= long("0xA050",16) # Transparency value of material +MAT_SELF_ILLUM = long("0xA080",16) # Self Illumination value of material +MAT_WIRE = long("0xA085",16) # Only render's wireframe + +MAT_TEXTURE_MAP = long("0xA200",16) # This is a header for a new texture map +MAT_SPECULAR_MAP= long("0xA204",16) # This is a header for a new specular map +MAT_OPACITY_MAP = long("0xA210",16) # This is a header for a new opacity map +MAT_REFLECTION_MAP= long("0xA220",16) # This is a header for a new reflection map +MAT_BUMP_MAP = long("0xA230",16) # This is a header for a new bump map +MAT_MAP_FILENAME= long("0xA300",16) # This holds the file name of the texture +#lots more to add here for maps + +###################################################### +# Globals +###################################################### +TEXTURE_DICT={} +MATERIAL_DICT={} + + +###################################################### +# Chunk Class +###################################################### +class chunk: + ID=0 + length=0 + bytes_read=0 + + binary_format="<HI" + + def __init__(self): + self.ID=0 + self.length=0 + self.bytes_read=0 + + def dump(self): + print "ID in hex: ", hex(self.ID) + print "length: ", self.length + print "bytes_read: ", self.bytes_read + + +###################################################### +# Helper functions +###################################################### +def read_chunk(file, chunk): + temp_data=file.read(struct.calcsize(chunk.binary_format)) + data=struct.unpack(chunk.binary_format, temp_data) + chunk.ID=data[0] + chunk.length=data[1] + chunk.bytes_read=6 + +def skip_to_end(file, skip_chunk): + buffer_size=skip_chunk.length-skip_chunk.bytes_read + binary_format=str(buffer_size)+"c" + temp_data=file.read(struct.calcsize(binary_format)) + skip_chunk.bytes_read+=buffer_size + +def read_string(file): + s="" + index=0 + #read the first character + temp_data=file.read(1) + data=struct.unpack("c", temp_data) + s=s+(data[0]) + #read in the characters till we get a null character + while(ord(s[index])!=0): + index+=1 + temp_data=file.read(1) + data=struct.unpack("c", temp_data) + s=s+(data[0]) + the_string=s[:-1] #remove the null character from the string + return str(the_string) + +def getUniqueName(name): + newName = name + uniqueInt = 0 + while 1: + try: + ob = Object.Get(newName) + # Okay, this is working, so lets make a new name + newName = '%s.%d' % (name, uniqueInt) + uniqueInt +=1 + except AttributeError: + if newName not in NMesh.GetNames(): + return newName + else: + newName = '%s.%d' % (name, uniqueInt) + uniqueInt +=1 + +def add_texture_to_material(image, texture, material, mapto): + if mapto=="DIFFUSE": + map=Texture.MapTo.COL + elif mapto=="SPECULAR": + map=Texture.MapTo.SPEC + elif mapto=="OPACITY": + map=Texture.MapTo.ALPHA + elif mapto=="BUMP": + map=Texture.MapTo.NOR + else: + print "/tError: Cannot map to ", mapto + return + + texture.setImage(image) + texture_list=material.getTextures() + index=0 + for tex in texture_list: + if tex==None: + material.setTexture(index,texture,Texture.TexCo.OBJECT,map) + return + else: + index+=1 + if index>10: + print "/tError: Cannot add diffuse map. Too many textures" + +###################################################### +# Process an object (tri-mesh, Camera, or Light) +###################################################### +def process_object_block(file, previous_chunk, object_list): + # Localspace variable names, faster. + STRUCT_SIZE_2FLOAT = struct.calcsize("2f") + STRUCT_SIZE_3FLOAT = struct.calcsize("3f") + STRUCT_SIZE_UNSIGNED_SHORT = struct.calcsize("H") + STRUCT_SIZE_4UNSIGNED_SHORT = struct.calcsize("4H") + STRUCT_SIZE_4x3MAT = struct.calcsize("ffffffffffff") + + #spare chunks + new_chunk=chunk() + temp_chunk=chunk() + + global TEXURE_DICT + global MATERIAL_DICT + + #don't know which one we're making, so let's have a place for one of each + new_mesh=None + new_light=None + new_camera=None + + #all objects have a name (first thing) + tempName = str(read_string(file)) + obj_name = getUniqueName( tempName ) + previous_chunk.bytes_read += (len(tempName)+1) + + while (previous_chunk.bytes_read<previous_chunk.length): + read_chunk(file, new_chunk) + + if (new_chunk.ID==OBJECT_MESH): + new_mesh=Blender.NMesh.New(obj_name) + while (new_chunk.bytes_read<new_chunk.length): + read_chunk(file, temp_chunk) + + if (temp_chunk.ID==MESH_VERTICES): + temp_data=file.read(STRUCT_SIZE_UNSIGNED_SHORT) + data=struct.unpack("H", temp_data) + temp_chunk.bytes_read+=2 + num_verts=data[0] + for counter in range (num_verts): + temp_data=file.read(STRUCT_SIZE_3FLOAT) + temp_chunk.bytes_read += STRUCT_SIZE_3FLOAT + data=struct.unpack("3f", temp_data) + v=NMesh.Vert(data[0],data[1],data[2]) + new_mesh.verts.append(v) + + elif (temp_chunk.ID==MESH_FACES): + temp_data=file.read(STRUCT_SIZE_UNSIGNED_SHORT) + data=struct.unpack("H", temp_data) + temp_chunk.bytes_read+=2 + num_faces=data[0] + for counter in range(num_faces): + temp_data=file.read(STRUCT_SIZE_4UNSIGNED_SHORT) + temp_chunk.bytes_read += STRUCT_SIZE_4UNSIGNED_SHORT #4 short ints x 2 bytes each + data=struct.unpack("4H", temp_data) + #insert the mesh info into the faces, don't worry about data[3] it is a 3D studio thing + f = NMesh.Face( [new_mesh.verts[data[i]] for i in xrange(3)] ) + f.uv = [ tuple(new_mesh.verts[data[i]].uvco[:2]) for i in xrange(3) ] + new_mesh.faces.append(f) + + elif (temp_chunk.ID==MESH_MATERIAL): + material_name="" + material_name=str(read_string(file)) + temp_chunk.bytes_read += len(material_name)+1 # remove 1 null character. + material_found=0 + for mat in Material.Get(): + if(mat.name==material_name): + if len(new_mesh.materials) >= 15: + print "\tCant assign more than 16 materials per mesh, keep going..." + break + else: + meshHasMat = 0 + for myMat in new_mesh.materials: + if myMat.name == mat.name: + meshHasMat = 1 + if meshHasMat == 0: + new_mesh.addMaterial(mat) + material_found=1 + #figure out what material index this is for the mesh + for mat_counter in range(len(new_mesh.materials)): + if new_mesh.materials[mat_counter].name == material_name: + mat_index=mat_counter + break # get out of this for loop so we don't accidentally set material_found back to 0 + else: + material_found=0 + + if material_found == 1: + #read the number of faces using this material + temp_data=file.read(STRUCT_SIZE_UNSIGNED_SHORT) + data=struct.unpack("H", temp_data) + temp_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT + num_faces_using_mat=data[0] + #list of faces using mat + for face_counter in range(num_faces_using_mat): + temp_data=file.read(STRUCT_SIZE_UNSIGNED_SHORT) + temp_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT + data=struct.unpack("H", temp_data) + new_mesh.faces[data[0]].materialIndex = mat_index + try: + mname = MATERIAL_DICT[mat.name] + new_mesh.faces[data[0]].image = TEXTURE_DICT[mname] + except: + continue + else: + #read past the information about the material you couldn't find + skip_to_end(file,temp_chunk) + + elif (new_chunk.ID == MESH_UV): + temp_data=file.read(STRUCT_SIZE_UNSIGNED_SHORT) + data=struct.unpack("H", temp_data) + temp_chunk.bytes_read+=2 + num_uv=data[0] + + for counter in range(num_uv): + temp_data=file.read(STRUCT_SIZE_2FLOAT) + temp_chunk.bytes_read += STRUCT_SIZE_2FLOAT #2 float x 4 bytes each + data=struct.unpack("2f", temp_data) + #insert the insert the UV coords in the vertex data + new_mesh.verts[counter].uvco = data + + elif (new_chunk.ID == MESH_TRANS_MATRIX): + temp_data=file.read(STRUCT_SIZE_4x3MAT) + data = list( struct.unpack("ffffffffffff", temp_data) ) + temp_chunk.bytes_read += STRUCT_SIZE_4x3MAT + new_matrix = Blender.Mathutils.Matrix(\ + data[:3] + [0],\ + data[3:6] + [0],\ + data[6:9] + [0],\ + data[9:] + [1]) + new_mesh.setMatrix(new_matrix) + else: + skip_to_end(file, temp_chunk) + + new_chunk.bytes_read+=temp_chunk.bytes_read + + elif (new_chunk.ID==OBJECT_LIGHT): + skip_to_end(file,new_chunk) + + elif (new_chunk.ID==OBJECT_CAMERA): + skip_to_end(file,new_chunk) + + else: #don't know what kind of object it is + skip_to_end(file,new_chunk) + + if new_mesh!=None: + object_list.append(NMesh.PutRaw(new_mesh)) + if new_light!=None: + object_list.append(new_light) + if new_camera!=None: + object_list.append(new_camera) + + previous_chunk.bytes_read+=new_chunk.bytes_read + +###################################################### +# Process a Material +###################################################### +def process_material_block(file, previous_chunk): + # Localspace variable names, faster. + STRUCT_SIZE_3BYTE = struct.calcsize("3B") + STRUCT_SIZE_UNSIGNED_SHORT = struct.calcsize("H") + + #spare chunks + new_chunk=chunk() + temp_chunk=chunk() + + global TEXURE_DICT + global MATERIAL_DICT + + new_material=Blender.Material.New() + + while (previous_chunk.bytes_read<previous_chunk.length): + #read the next chunk + read_chunk(file, new_chunk) + + if (new_chunk.ID==MAT_NAME): + material_name="" + material_name=str(read_string(file)) + new_chunk.bytes_read+=(len(material_name)+1) #plus one for the null character that ended the string + new_material.setName(material_name) + MATERIAL_DICT[material_name] = new_material.name + + elif (new_chunk.ID==MAT_AMBIENT): + read_chunk(file, temp_chunk) + temp_data=file.read(STRUCT_SIZE_3BYTE) + data=struct.unpack("3B", temp_data) + temp_chunk.bytes_read+=3 + new_material.mirCol = [float(col)/255 for col in data] # data [0,1,2] == rgb + new_chunk.bytes_read+=temp_chunk.bytes_read + + elif (new_chunk.ID==MAT_DIFFUSE): + read_chunk(file, temp_chunk) + temp_data=file.read(STRUCT_SIZE_3BYTE) + data=struct.unpack("3B", temp_data) + temp_chunk.bytes_read+=3 + new_material.rgbCol = [float(col)/255 for col in data] # data [0,1,2] == rgb + new_chunk.bytes_read+=temp_chunk.bytes_read + + elif (new_chunk.ID==MAT_SPECULAR): + read_chunk(file, temp_chunk) + temp_data=file.read(STRUCT_SIZE_3BYTE) + data=struct.unpack("3B", temp_data) + temp_chunk.bytes_read+=3 + new_material.specCol = [float(col)/255 for col in data] # data [0,1,2] == rgb + new_chunk.bytes_read+=temp_chunk.bytes_read + + elif (new_chunk.ID==MAT_TEXTURE_MAP): + new_texture=Blender.Texture.New('Diffuse') + new_texture.setType('Image') + while (new_chunk.bytes_read<new_chunk.length): + read_chunk(file, temp_chunk) + + if (temp_chunk.ID==MAT_MAP_FILENAME): + texture_name="" + texture_name=str(read_string(file)) + try: + img = Image.Load(texture_name) + TEXTURE_DICT[new_material.name]=img + except IOError: + fname = os.path.join( os.path.dirname(FILENAME), texture_name) + try: + img = Image.Load(fname) + TEXTURE_DICT[new_material.name]=img + except IOError: + print "\tERROR: failed to load image ",texture_name + TEXTURE_DICT[new_material.name] = None # Dummy + img=Blender.Image.New(fname,1,1,24) #blank image + new_chunk.bytes_read += (len(texture_name)+1) #plus one for the null character that gets removed + + else: + skip_to_end(file, temp_chunk) + + new_chunk.bytes_read+=temp_chunk.bytes_read + + #add the map to the material in the right channel + add_texture_to_material(img, new_texture, new_material, "DIFFUSE") + + elif (new_chunk.ID==MAT_SPECULAR_MAP): + new_texture=Blender.Texture.New('Specular') + new_texture.setType('Image') + while (new_chunk.bytes_read<new_chunk.length): + read_chunk(file, temp_chunk) + + if (temp_chunk.ID==MAT_MAP_FILENAME): + texture_name="" + texture_name=str(read_string(file)) + try: + img = Image.Load(texture_name) + TEXTURE_DICT[new_material.name]=img + except IOError: + fname = os.path.join( os.path.dirname(FILENAME), texture_name) + try: + img = Image.Load(fname) + TEXTURE_DICT[new_material.name]=img + except IOError: + print "\tERROR: failed to load image ",texture_name + TEXTURE_DICT[new_material.name] = None # Dummy + img=Blender.Image.New(fname,1,1,24) #blank image + new_chunk.bytes_read += (len(texture_name)+1) #plus one for the null character that gets removed + else: + skip_to_end(file, temp_chunk) + + new_chunk.bytes_read+=temp_chunk.bytes_read + + #add the map to the material in the right channel + add_texture_to_material(img, new_texture, new_material, "SPECULAR") + + elif (new_chunk.ID==MAT_OPACITY_MAP): + new_texture=Blender.Texture.New('Opacity') + new_texture.setType('Image') + while (new_chunk.bytes_read<new_chunk.length): + read_chunk(file, temp_chunk) + + if (temp_chunk.ID==MAT_MAP_FILENAME): + texture_name="" + texture_name=str(read_string(file)) + try: + img = Image.Load(texture_name) + TEXTURE_DICT[new_material.name]=img + except IOError: + fname = os.path.join( os.path.dirname(FILENAME), texture_name) + try: + img = Image.Load(fname) + TEXTURE_DICT[new_material.name]=img + except IOError: + print "\tERROR: failed to load image ",texture_name + TEXTURE_DICT[new_material.name] = None # Dummy + img=Blender.Image.New(fname,1,1,24) #blank image + new_chunk.bytes_read += (len(texture_name)+1) #plus one for the null character that gets removed + else: + skip_to_end(file, temp_chunk) + + new_chunk.bytes_read+=temp_chunk.bytes_read + + #add the map to the material in the right channel + add_texture_to_material(img, new_texture, new_material, "OPACITY") + + elif (new_chunk.ID==MAT_BUMP_MAP): + new_texture=Blender.Texture.New('Bump') + new_texture.setType('Image') + while (new_chunk.bytes_read<new_chunk.length): + read_chunk(file, temp_chunk) + + if (temp_chunk.ID==MAT_MAP_FILENAME): + texture_name="" + texture_name=str(read_string(file)) + try: + img = Image.Load(texture_name) + TEXTURE_DICT[new_material.name]=img + except IOError: + fname = os.path.join( os.path.dirname(FILENAME), texture_name) + try: + img = Image.Load(fname) + TEXTURE_DICT[new_material.name]=img + except IOError: + print "\tERROR: failed to load image ",texture_name + TEXTURE_DICT[new_material.name] = None # Dummy + img=Blender.Image.New(fname,1,1,24) #blank image + new_chunk.bytes_read += (len(texture_name)+1) #plus one for the null character that gets removed + else: + skip_to_end(file, temp_chunk) + + new_chunk.bytes_read+=temp_chunk.bytes_read + + #add the map to the material in the right channel + add_texture_to_material(img, new_texture, new_material, "BUMP") + + elif (new_chunk.ID==MAT_TRANSPARENCY): + read_chunk(file, temp_chunk) + temp_data=file.read(STRUCT_SIZE_UNSIGNED_SHORT) + data=struct.unpack("H", temp_data) + temp_chunk.bytes_read+=2 + new_material.setAlpha(1-(float(data[0])/100)) + new_chunk.bytes_read+=temp_chunk.bytes_read + + else: + skip_to_end(file,new_chunk) + + previous_chunk.bytes_read+=new_chunk.bytes_read + +###################################################### +# process a main chunk +###################################################### +def process_main_chunk(file,previous_chunk,new_object_list): + + #spare chunks + new_chunk=chunk() + temp_chunk=chunk() + + #Go through the main chunk + while (previous_chunk.bytes_read<previous_chunk.length): + read_chunk(file, new_chunk) + + if (new_chunk.ID==VERSION): + temp_data=file.read(struct.calcsize("I")) + data=struct.unpack("I", temp_data) + version=data[0] + new_chunk.bytes_read+=4 #read the 4 bytes for the version number + if (version>3): #this loader works with version 3 and below, but may not with 4 and above + print "\tNon-Fatal Error: Version greater than 3, may not load correctly: ", version + + elif (new_chunk.ID==EDITOR_BLOCK): + while(new_chunk.bytes_read<new_chunk.length): + read_chunk(file, temp_chunk) + if (temp_chunk.ID==MATERIAL_BLOCK): + process_material_block(file, temp_chunk) + elif (temp_chunk.ID==OBJECT_BLOCK): + process_object_block(file, temp_chunk,new_object_list) + else: + skip_to_end(file,temp_chunk) + + new_chunk.bytes_read+=temp_chunk.bytes_read + else: + skip_to_end(file,new_chunk) + + previous_chunk.bytes_read+=new_chunk.bytes_read + +#*********************************************** +# main entry point for loading 3ds files +#*********************************************** +def load_3ds (filename): + current_chunk=chunk() + print "--------------------------------" + print 'Importing "%s"' % filename + time1 = Blender.sys.time() #for timing purposes + file=open(filename,"rb") + new_object_list = [] + + global FILENAME + FILENAME=filename + + read_chunk(file, current_chunk) + + if (current_chunk.ID!=PRIMARY): + print "\tFatal Error: Not a valid 3ds file: ", filename + file.close() + return + + process_main_chunk(file, current_chunk, new_object_list) + + # Select all new objects. + for ob in new_object_list: ob.sel = 1 + + print 'finished importing: "%s" in %.4f sec.' % (filename, (Blender.sys.time()-time1)) + file.close() + +#*********************************************** +# MAIN +#*********************************************** +def my_callback(filename): + load_3ds(filename) + +Blender.Window.FileSelector(my_callback, "Import 3DS", '*.3ds') + +# For testing compatibility +''' +TIME = Blender.sys.time() +import os +for _3ds in os.listdir('/3ds/'): + if _3ds.lower().endswith('3ds'): + print _3ds + newScn = Scene.New(_3ds) + newScn.makeCurrent() + my_callback('/3ds/' + _3ds) + +print "TOTAL TIME: ", Blender.sys.time() - TIME +''' diff --git a/release/scripts/blenderLipSynchro.py b/release/scripts/blenderLipSynchro.py new file mode 100644 index 00000000000..8c66e6779ab --- /dev/null +++ b/release/scripts/blenderLipSynchro.py @@ -0,0 +1,602 @@ +#!BPY + +""" +Name: 'BlenderLipSynchro' +Blender: 239 +Group: 'Animation' +Tooltip: 'Import phonemes from Papagayo or JLipSync for lip synchronisation' +""" + +__author__ = "Dienben: Benoit Foucque dienben_mail@yahoo.fr" +__url__ = ("blenderLipSynchro Blog, http://blenderlipsynchro.blogspot.com/", +"Papagayo (Python), http://www.lostmarble.com/papagayo/index.shtml", +"JLipSync (Java), http://jlipsync.sourceforge.net/") +__version__ = "1.2" +__bpydoc__ = """\ +Description: + +This script imports Voice Export made by Papagayo or JLipSync and maps the export with your shapes. + +Usage: + +Import a Papagayo or JLipSync voice export file and link it with your shapes. + +Note:<br> +- Naturally, you need files exported from one of the supported lip synching +programs. Check their sites to learn more and download them. + +""" + +# -------------------------------------------------------------------------- +# BlenderLipSynchro +# -------------------------------------------------------------------------- +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# ***** END GPL LICENCE BLOCK ***** +# -------------------------------------------------------------------------- + + + +#il y a 3 etapes +#la deuxieme on charge le dictionnaire de correspondance +#la troisieme on fait le choix des correpondance +#la quatrieme on construit les cles a partir du fichiers frame + +#there are 3 stage +#the second one load the mapping dictionnary +#the tird make the mapping +#the fourth make the key in the IPO Curve + +#voici mes differents imports +#the imports +import os +import Blender + +from Blender import Ipo +from Blender.Draw import * +from Blender.BGL import * + + + +#ici commencent mes fonctions +#here begin my functions +#cette fonction trace l'interface graphique +#this functions draw the User interface +def trace(): + #voici mes variables pouvant etre modifie + #my variables + global fichier_dico,repertoire_dictionaire,iponame,repertoire_phoneme,fichier_text,nbr_phoneme + global let01, let02, let03, let04,let05, let06, let07, let08, let09, let10 + global let11, let12, let13, let14,let15, let16, let17, let18, let19, let20 + global let21, let22, let23, let24 + + global let01selectkey,let02selectkey,let03selectkey,let04selectkey,let05selectkey + global let06selectkey,let07selectkey,let08selectkey,let09selectkey,let10selectkey,let11selectkey + global let12selectkey,let13selectkey,let14selectkey,let15selectkey,let16selectkey,let17selectkey + global let18selectkey,let19selectkey,let20selectkey,let21selectkey,let22selectkey,let23selectkey + global let24selectkey + + glClearColor(0.4,0.5,0.6 ,0.0) + glClear(GL_COLOR_BUFFER_BIT) + + glColor3d(1,1,1) + glRasterPos2i(87, 375) + Text("Blendersynchro V 1.1") + glColor3d(1,1,1) + glRasterPos2i(84, 360) + Text("Programation: Dienben") + + glColor3d(0,0,0) + glRasterPos2i(13, 342) + Text("Papagayo File importer") + glColor3d(0,0,0) + glRasterPos2i(13, 326) + Text("Thanks to Chris Clawson and Liubomir Kovatchev") + + glColor3d(1,1,1) + glRasterPos2i(5, 320) + Text("_______________________________________________________") + glColor3d(0,0,0) + glRasterPos2i(6, 318) + Text("_______________________________________________________") + + + if (etape==1): + #cette etape permet de choisi la correspondance entre les phonemes et les cles + #this stage offer the possibility to choose the mapping between phonems and shapes + + glColor3d(1,1,1) + glRasterPos2i(140, 300) + Text("Objet: "+Blender.Object.GetSelected()[0].getName() ) + + glColor3d(1,1,1) + glRasterPos2i(5, 215) + Text("Assign phonems to shapes:") + + #on mesure la taille de la liste de phonemes + #this is the lenght of the phonem list + nbr_phoneme=len(liste_phoneme) + + #on dessine les listes de choix + #we draw the choice list + + let01 = String(" ", 4, 5, 185, 30, 16, liste_phoneme[0], 3) + glColor3d(0,0,0) + glRasterPos2i(40, 188) + Text("=") + let01selectkey = Menu(key_menu, 50, 50, 185, 70, 16, let01selectkey.val) + + + let02 = String(" ", 4, 150, 185, 30, 16, liste_phoneme[1], 2) + glColor3d(0,0,0) + glRasterPos2i(185, 188) + Text("=") + let02selectkey = Menu(key_menu, 51, 195, 185, 70, 16, let02selectkey.val) + + + let03 = String(" ", 4, 5, 165, 30, 16, liste_phoneme[2], 2) + glColor3d(0,0,0) + glRasterPos2i(40, 168) + Text("=") + let03selectkey = Menu(key_menu, 52, 50, 165, 70, 16, let03selectkey.val) + + + let04 = String(" ", 4, 150, 165, 30, 16, liste_phoneme[3], 2) + glColor3d(0,0,0) + glRasterPos2i(185, 168) + Text("=") + let04selectkey = Menu(key_menu, 53, 195, 165, 70, 16, let04selectkey.val) + + + let05 = String(" ", 4, 5, 145, 30, 16, liste_phoneme[4], 2) + glColor3d(0,0,0) + glRasterPos2i(40, 148) + Text("=") + let05selectkey = Menu(key_menu, 54, 50, 145, 70, 16, let05selectkey.val) + + + let06 = String(" ", 4, 150, 145, 30, 16, liste_phoneme[5], 2) + glColor3d(0,0,0) + glRasterPos2i(185, 148) + Text("=") + let06selectkey = Menu(key_menu, 55, 195, 145, 70, 16, let06selectkey.val) + + + let07 = String(" ", 4, 5, 125, 30, 16, liste_phoneme[6], 2) + glColor3d(0,0,0) + glRasterPos2i(40, 128) + Text("=") + let07selectkey = Menu(key_menu, 56, 50, 125, 70, 16, let07selectkey.val) + + # + let08 = String(" ", 4, 150, 125, 30, 16, liste_phoneme[7], 2) + glColor3d(0,0,0) + glRasterPos2i(185, 128) + Text("=") + let08selectkey = Menu(key_menu, 57, 195, 125, 70, 16,let08selectkey.val) + + + let09 = String(" ", 4, 5, 105, 30, 16, liste_phoneme[8], 2) + glColor3d(0,0,0) + glRasterPos2i(40, 108) + Text("=") + let09selectkey = Menu(key_menu, 58, 50, 105, 70, 16,let09selectkey.val) + + + let10 = String(" ", 4, 150, 105, 30, 16, liste_phoneme[9], 2) + glColor3d(0,0,0) + glRasterPos2i(185, 108) + Text("=") + let10selectkey = Menu(key_menu, 59, 195, 105, 70, 16, let10selectkey.val) + + #soft_type = 0:Papagayo + #soft_type = 1:JLipSync + if (soft_type==1): + let11 = String(" ", 4, 5, 85, 30, 16, liste_phoneme[10], 2) + glColor3d(0,0,0) + glRasterPos2i(40, 88) + Text("=") + let11selectkey = Menu(key_menu, 60, 50, 85, 70, 16, let11selectkey.val) + + let12 = String(" ", 4, 150, 85, 30, 16, liste_phoneme[11], 2) + glColor3d(0,0,0) + Text("=") + let12selectkey = Menu(key_menu, 61, 195, 85, 70, 16, let12selectkey.val) + + let13 = String(" ", 4, 5, 65, 30, 16, liste_phoneme[12], 2) + glColor3d(0,0,0) + glRasterPos2i(40, 68) + Text("=") + let13selectkey = Menu(key_menu, 62, 50, 65, 70, 16, let13selectkey.val) + + let14 = String(" ", 4, 150, 65, 30, 16, liste_phoneme[13], 2) + glColor3d(0,0,0) + glRasterPos2i(185, 68) + Text("=") + let14selectkey = Menu(key_menu, 63, 195, 65, 70, 16, let14selectkey.val) + + let15 = String(" ", 4, 5, 45, 30, 16, liste_phoneme[14], 2) + glColor3d(0,0,0) + glRasterPos2i(40, 48) + Text("=") + let15selectkey = Menu(key_menu, 64, 50, 45, 70, 16, let15selectkey.val) + + let16 = String(" ", 4, 150, 45, 30, 16, liste_phoneme[15], 2) + glColor3d(0,0,0) + glRasterPos2i(185, 48) + Text("=") + let16selectkey = Menu(key_menu, 65, 195, 45, 70, 16, let16selectkey.val) + + let17 = String(" ", 4, 295, 185, 30, 16, liste_phoneme[16], 2) + glColor3d(0,0,0) + glRasterPos2i(330, 188) + Text("=") + let17selectkey = Menu(key_menu, 66, 340, 185, 70, 16, let17selectkey.val) + + let18 = String(" ", 4, 440, 185, 70, 16, liste_phoneme[17], 8) + glColor3d(0,0,0) + glRasterPos2i(515, 188) + Text("=") + let18selectkey = Menu(key_menu, 67, 525, 185, 70, 16, let18selectkey.val) + + let19 = String(" ", 4, 295, 165, 30, 16, liste_phoneme[18], 2) + glColor3d(0,0,0) + glRasterPos2i(330, 168) + Text("=") + let19selectkey = Menu(key_menu, 68, 340, 165, 70, 16, let19selectkey.val) + + let20 = String(" ", 4, 440, 165, 70, 16, liste_phoneme[19], 8) + glColor3d(0,0,0) + glRasterPos2i(515, 168) + Text("=") + let20selectkey = Menu(key_menu, 69, 525, 165, 70, 16, let20selectkey.val) + + let21 = String(" ", 4, 295, 145, 30, 16, liste_phoneme[20], 2) + glColor3d(0,0,0) + glRasterPos2i(330, 148) + Text("=") + let21selectkey = Menu(key_menu, 70, 340, 145, 70, 16, let21selectkey.val) + + let22 = String(" ", 4, 440, 145, 70, 16, liste_phoneme[21], 8) + glColor3d(0,0,0) + glRasterPos2i(515, 148) + Text("=") + let22selectkey = Menu(key_menu, 71, 525, 145, 70, 16, let22selectkey.val) + + let23 = String(" ", 4, 295, 125, 30, 16, liste_phoneme[22], 2) + glColor3d(0,0,0) + glRasterPos2i(330, 128) + Text("=") + let23selectkey = Menu(key_menu, 72, 340, 125, 70, 16,let23selectkey.val) + + let24 = String(" ", 4, 440, 125, 70, 16, liste_phoneme[23], 8) + glColor3d(0,0,0) + glRasterPos2i(515, 128) + Text("=") + let24selectkey = Menu(key_menu, 73, 525, 125, 70, 16, let24selectkey.val) + + + Button("Import Text", 3, 155, 5, 145, 22) + Button("Choose Voice Export", 4, 120, 250, 250, 22) + + + if (etape==2): + glColor3d(1,1,1) + glRasterPos2i(125, 200) + Text("Operation Completed") + + if (etape==0): + glColor3d(1,1,1) + glRasterPos2i(125, 200) + Text("Please select a Mesh'Object and Create all the IPO Curves for your Shapes") + + if (etape==3): + Button("Papagayo", 5, 155, 250, 250, 22) + Button("JlipSync", 6, 155, 225, 250, 22) + + glColor3d(1,1,1) + glRasterPos2i(6, 40) + Text("_______________________________________________________") + glColor3d(0,0,0) + glRasterPos2i(6, 38) + Text("_______________________________________________________") + + Button("Exit", 1, 305, 5, 80, 22) + + + + +#cette fonction sur evenement quite en cas d'ESC +#this functions catch the ESC event and quit +def event(evt,val): + if (evt == ESCKEY and not val): Exit() + +#cette fonction gere les evenements +#the event functions +def bevent(evt): + global etape,soft_type,liste_phoneme + + if (evt == 1): + Exit() + + + elif (evt == 4): + #c'est le choix de l'export pamela + #we choose the papagayo export + Blender.Window.FileSelector(selectionner_export_papagayo,"Choose Export") + + + elif (evt == 3): + #c'est l'import Papagayo + #we import + lecture_chaine(mon_fichier_export_pamela,dico_phoneme_export_pamela) + construction_dico_correspondance() + construction_lipsynchro() + #on change d'etape + #we change the stage + etape=2 + + elif (evt == 5): + #we choose papagayo + soft_type=0 + liste_phoneme=liste_phoneme_papagayo + etape=1 + + elif (evt == 6): + #we choose jlipsync + soft_type=1 + liste_phoneme=liste_phoneme_jlipsinch + etape=1 + + Blender.Redraw() + +#cette fonction recupere le nom et le chemin du fichier dictionnaire +#we catch the name and the path of the dictionnary +def selectionner_export_papagayo(filename): + global mon_fichier_export_pamela + #debut + mon_fichier_export_pamela=filename + +#fonction de lecture de la liste frame phoneme +#we read the frame and phonems +def lecture_chaine(fichier,liste): + mon_fichier=open(fichier) + + #je lis la premiere ligne qui contiens la version de moho + #first, we read the moho version + mon_fichier.readline() + + #je lis jusqu'a la fin + #then we read until the end of the file + while 1: + ma_ligne=mon_fichier.readline() + if ma_ligne=='': + break + decoup=ma_ligne.split() + liste[decoup[0]]=decoup[1] + print liste + + + + +#fonction qui construit la liste dictionnaire simple +#we make the dictionnary +def construction_dictionnaire_phoneme(): + index_liste=0 + #je transforme mon dictionnaire en list de tulpes + #we transform the list in tulpes + ma_liste=dico_phoneme.items() + #je parcours ma liste a la recherche d'elements non existant + #we read the list to find non existing elements + print dico_phoneme + for index in range(len(ma_liste)): + if ma_liste[index][1] not in liste_phoneme: + liste_phoneme[index_liste:index_liste]=[ma_liste[index][1]] + index_liste=index_liste+1 + print liste_phoneme + + + +#cette fonction recupere les courbes cible +#this functon catch the IPO curve +def recuperation_courbe(): + global key_menu,dico_key + + #on recupere le nom des shapes + #we catch the shapes + key=Blender.Object.GetSelected()[0].getData().getKey().getBlocks() + for n in range(len(key)): + #on vire la première cle (en effet basic n'est pas une cle en tant que telle) + #we threw away the basic shapes + if (n>0): + key_menu=key_menu+key[n].name + " %x" + str(n-1) + "|" + dico_key[str(n-1)]=Blender.Object.GetSelected()[0].getData().getKey().getIpo().getCurves()[n-1] + + + print "dico_key" + print dico_key + print 'end dico_key' + +#cette fonction construit un dictionnaire de correspondance entre les phonemes prononces et les cles a utiliser +#we make the dictionnary for the mapping between shapes and phonems +def construction_dico_correspondance(): + global dico_correspondance + #je parcours les phonemes + #we read the phonems + dico_correspondance[liste_phoneme[0]]=dico_key[str(let01selectkey.val)] + dico_correspondance[liste_phoneme[1]]=dico_key[str(let02selectkey.val)] + dico_correspondance[liste_phoneme[2]]=dico_key[str(let03selectkey.val)] + dico_correspondance[liste_phoneme[3]]=dico_key[str(let04selectkey.val)] + dico_correspondance[liste_phoneme[4]]=dico_key[str(let05selectkey.val)] + dico_correspondance[liste_phoneme[5]]=dico_key[str(let06selectkey.val)] + dico_correspondance[liste_phoneme[6]]=dico_key[str(let07selectkey.val)] + dico_correspondance[liste_phoneme[7]]=dico_key[str(let08selectkey.val)] + dico_correspondance[liste_phoneme[8]]=dico_key[str(let09selectkey.val)] + dico_correspondance[liste_phoneme[9]]=dico_key[str(let10selectkey.val)] + + if (soft_type==1): + dico_correspondance[liste_phoneme[10]]=dico_key[str(let11selectkey.val)] + dico_correspondance[liste_phoneme[11]]=dico_key[str(let12selectkey.val)] + dico_correspondance[liste_phoneme[12]]=dico_key[str(let13selectkey.val)] + dico_correspondance[liste_phoneme[13]]=dico_key[str(let14selectkey.val)] + dico_correspondance[liste_phoneme[14]]=dico_key[str(let15selectkey.val)] + dico_correspondance[liste_phoneme[15]]=dico_key[str(let16selectkey.val)] + dico_correspondance[liste_phoneme[16]]=dico_key[str(let17selectkey.val)] + dico_correspondance[liste_phoneme[17]]=dico_key[str(let18selectkey.val)] + dico_correspondance[liste_phoneme[18]]=dico_key[str(let19selectkey.val)] + dico_correspondance[liste_phoneme[19]]=dico_key[str(let20selectkey.val)] + dico_correspondance[liste_phoneme[20]]=dico_key[str(let21selectkey.val)] + dico_correspondance[liste_phoneme[21]]=dico_key[str(let22selectkey.val)] + dico_correspondance[liste_phoneme[22]]=dico_key[str(let23selectkey.val)] + dico_correspondance[liste_phoneme[23]]=dico_key[str(let24selectkey.val)] + + print dico_correspondance + + +#cette fonction ajoute un points a la cle donnee a la frame donnee +#we add a point to the IPO curve Target +def ajoute_point(cle,frame,valeur): + cle.setInterpolation('Linear') + cle.addBezier((frame,valeur)) + cle.Recalc() + +#cette fonction parcours le dictionnaire des frame à ajouter et construit les points +#we add all the point to the IPO Curve +def construction_lipsynchro(): + print "je construit" + doublet_old="" + #construction de la liste des frame + cpt=0 + liste_frame=[] + for frame in dico_phoneme_export_pamela: + liste_frame.append(int(frame)) + cpt=cpt+1 + liste_frame.sort() + print "listeframe" + print liste_frame + print "fini" + + for doublet in liste_frame: + ajoute_point(dico_correspondance[dico_phoneme_export_pamela[str(doublet)]],doublet,1) + if (doublet_old==""): + ajoute_point(dico_correspondance[dico_phoneme_export_pamela[str(doublet)]],(doublet-2),0) + if (doublet_old!=''): + if (dico_correspondance[dico_phoneme_export_pamela[str(doublet)]]!=dico_correspondance[dico_phoneme_export_pamela[doublet_old]]): + print "doublet:"+str(doublet) + print "doublet old:"+doublet_old + ajoute_point(dico_correspondance[dico_phoneme_export_pamela[doublet_old]],(int(doublet_old)+2),0) + ajoute_point(dico_correspondance[dico_phoneme_export_pamela[str(doublet)]],(doublet-2),0) + doublet_old=str(doublet) + + +#end of my functions we begin the execution +#je commence l execution----------------------------------------------------------------------------------------------- +#voici mes variables + +#declaration et instanciation +#decleration and instanciation +#ce sont les repertoires +repertoire_dictionaire=Create('C:/') +repertoire_phoneme=Create('c:/') + +#ce sont ls fichiers +fichier_dico=Create("sample.mot") +fichier_text=Create("") + +#voici mon objet de travail +objet_travail=Create(0) + +#my soft type +soft_type=1 + +#voici la liste des phoneme effectivement utilise +#the phonems'list +liste_phoneme_papagayo=['AI','E','O','U','FV','L','WQ','MBP','etc','rest'] +liste_phoneme_jlipsinch=['A','B','C','Closed','D','E','F','G','I','K','L','M','N','O','P','Q','R','S','SH','T','TH','U','V','W'] + +liste_phoneme=liste_phoneme_jlipsinch +#voici mon dictionnaire des frames o +dico_phoneme_export_pamela = Create(0) +dico_phoneme_export_pamela={} + + + +#voici mes cle +key_menu="" +dico_key={} + +#voici mes ipo +dico_bloc={} +iponame = Create(0) + +#voici mon dictionnaire de correspondance +dico_correspondance={} + +try: + #on verifie est bien une mesh et qu'il a des courbes + if ((Blender.Object.GetSelected()[0].getType()=='Mesh')): + #on verifie que l'objet a bien toute ses Courbes + if (len(Blender.Object.GetSelected()[0].getData().getKey().getBlocks())-1==Blender.Object.GetSelected()[0].getData().getKey().getIpo().getNcurves()): + etape=3 + #on lance la creation du dictionnaire + recuperation_courbe() + else: + print "not the good number of IPO Curve" + etape = 0 + else: + print "error: bad object Type:" + print Blender.Object.GetSelected()[0].getType() + etape = 0 +except: + print 'error: exception' + etape = 0 + + +#voici le fichier dictionnaire +mon_fichier_dico="" + +#voici le fichier export pamela +mon_fichier_export_pamela="" + + +let01selectkey = Create(0) +let02selectkey = Create(0) +let03selectkey = Create(0) +let04selectkey = Create(0) +let05selectkey = Create(0) +let06selectkey = Create(0) +let07selectkey = Create(0) +let08selectkey = Create(0) +let09selectkey = Create(0) +let10selectkey = Create(0) +let11selectkey = Create(0) +let12selectkey = Create(0) +let13selectkey = Create(0) +let14selectkey = Create(0) +let15selectkey = Create(0) +let16selectkey = Create(0) +let17selectkey = Create(0) +let18selectkey = Create(0) +let19selectkey = Create(0) +let20selectkey = Create(0) +let21selectkey = Create(0) +let22selectkey = Create(0) +let23selectkey = Create(0) +let24selectkey = Create(0) + + +Register (trace,event,bevent) diff --git a/release/scripts/flt_export.py b/release/scripts/flt_export.py new file mode 100755 index 00000000000..b5c5797fa42 --- /dev/null +++ b/release/scripts/flt_export.py @@ -0,0 +1,723 @@ +#!BPY + +# flt_export.py is an OpenFlight exporter for blender. +# Copyright (C) 2005 Greg MacDonald +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +""" Registration info for Blender menus: +Name: 'OpenFlight (.flt)...' +Blender: 237 +Group: 'Export' +Tip: 'Export to OpenFlight v16.0 (.flt)' +""" + +__author__ = "Greg MacDonald" +__version__ = "1.2 10/20/05" +__url__ = ("blender", "elysiun", "Author's homepage, http://sourceforge.net/projects/blight/") +__bpydoc__ = """\ +This script exports v16.0 OpenFlight files. OpenFlight is a +registered trademark of MultiGen-Paradigm, Inc. + +Run from "File->Export" menu. + +Options are available from Blender's "Scripts Config Editor," accessible through +the "Scripts->System" menu from the scripts window. + +Features:<br> +* Heirarchy retained.<br> +* Normals retained.<br> +* First texture exported.<br> +* Diffuse material color is exported as the face color, material color, or both +depending on the option settings.<br> +* Double sided faces are exported as two faces.<br> +* Object transforms exported. + +Things To Be Aware Of:<br> +* Object names are exported, not mesh or data names. +* Material indices that don't have a material associated with them will confuse the +exporter. If a warning appears about this, correct it by deleting the offending +material indices in Blender. + +What's Not Handled:<br> +* Animations.<br> +* Vetex colors.<br> +""" + +import Blender +from flt_filewalker import FltOut + +class ExporterOptions: + def __init__(self): + self.defaults = { 'Diffuse Color To OpenFlight Material': False, + 'Diffuse Color To OpenFlight Face': True} + + d = Blender.Registry.GetKey('flt_export', True) + + if d == None or d.keys() != self.defaults.keys(): + d = self.defaults + Blender.Registry.SetKey('flt_export', d, True) + + self.verbose = 1 + self.tolerance = 0.001 + self.use_mat_color = d['Diffuse Color To OpenFlight Material'] + self.use_face_color = d['Diffuse Color To OpenFlight Face'] + +options = ExporterOptions() + +FLOAT_TOLERANCE = options.tolerance + +identity_matrix = [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]] + +def is_identity(m): + for i in xrange(4): + for j in xrange(4): + if abs(m[i][j] - identity_matrix[i][j]) > FLOAT_TOLERANCE: + return False + return True + +class MaterialDesc: + def __init__(self): + self.name = 'Blender' + + # Colors, List of 3 floats. + self.diffuse = [1.0, 1.0, 1.0] + self.specular = [1.0, 1.0, 1.0] + + # Scalars + self.ambient = 0.1 # [0.0, 1.0] + self.emissive = 0.0 # [0.0, 1.0] + self.shininess = 32.0 # Range is [0.0, 128.0] + self.alpha = 1.0 # Range is [0.0, 1.0] + +class VertexDesc: + def __init__(self, co=None, no=None, uv=None): + if co: self.x, self.y, self.z = tuple(co) + else: self.x = self.y = self.z = 0.0 + if no: self.nx, self.ny, self.nz = tuple(no) + else: self.nx = self.ny = self.nz = 0.0 + if uv: self.u, self.v = tuple(uv) + else: self.u = self.v = 0.0 + +class GlobalResourceRepository: + def new_face_name(self): + self.face_name += 1 + return 'f%i' % (self.face_name-1) + + def vertex_count(self): + return len(self.vertex_lst) + + def request_vertex_desc(self, i): + return self.vertex_lst[i] + + def request_vertex_index(self, desc): + match = None + for i, v in enumerate(self.vertex_lst): + if\ + abs(v.x - desc.x) > FLOAT_TOLERANCE or\ + abs(v.y - desc.y) > FLOAT_TOLERANCE or\ + abs(v.z - desc.z) > FLOAT_TOLERANCE or\ + abs(v.nx - desc.nx) > FLOAT_TOLERANCE or\ + abs(v.ny - desc.ny) > FLOAT_TOLERANCE or\ + abs(v.nz - desc.nz) > FLOAT_TOLERANCE or\ + abs(v.u - desc.u) > FLOAT_TOLERANCE or\ + abs(v.v - desc.v) > FLOAT_TOLERANCE: + pass + else: + match = i + break + + if match != None: + return match + else: + self.vertex_lst.append(desc) + return len(self.vertex_lst) - 1 + + def request_texture_index(self, filename): + match = None + for i in xrange(len(self.texture_lst)): + if self.texture_lst[i] != filename: + continue + match = i + break + if match != None: + return match + else: + self.texture_lst.append(filename) + return len(self.texture_lst) - 1 + + def request_texture_filename(self, index): + return self.texture_lst[index] + + def texture_count(self): + return len(self.texture_lst) + + def request_material_index(self, desc): + match = None + for i in xrange(len(self.material_lst)): + if self.material_lst[i].diffuse != desc.diffuse: + continue + if self.material_lst[i].specular != desc.specular: + continue + if self.material_lst[i].ambient != desc.ambient: + continue + if self.material_lst[i].emissive != desc.emissive: + continue + if self.material_lst[i].shininess != desc.shininess: + continue + if self.material_lst[i].alpha != desc.alpha: + continue + match = i + break + + if match != None: + return i + else: + self.material_lst.append(desc) + return len(self.material_lst) - 1 + + def request_material_desc(self, index): + return self.material_lst[index] + + def material_count(self): + return len(self.material_lst) + + # Returns not actual index but one that includes intensity information. + # color_index = 127*intensity + 128*actual_index + def request_color_index(self, col): + r,g,b = tuple(col) + m = max(r, g, b) + if m > 0.0: + intensity = m / 1.0 + r = int(round(r/m * 255.0)) + g = int(round(g/m * 255.0)) + b = int(round(b/m * 255.0)) + brightest = [r, g, b] + else: + brightest = [255, 255, 255] + intensity = 0.0 + + match = None + for i in xrange(len(self.color_lst)): + if self.color_lst[i] != brightest: + continue + + match = i + break + + if match != None: + index = match + else: + length = len(self.color_lst) + if length <= 1024: + self.color_lst.append(brightest) + index = length + else: + if options.verbose >= 1: + print 'Warning: Exceeded max color limit.' + index = 0 + + color_index = int(round(127.0*intensity)) + 128*index + return color_index + + # Returns color from actual index. + def request_max_color(self, index): + return self.color_lst[index] + + def color_count(self): + return len(self.color_lst) + + def __init__(self): + self.vertex_lst = [] + self.texture_lst = [] + self.material_lst = [] + self.color_lst = [[255, 255, 255]] + self.face_name = 0 + +class Node: + # Gathers info from blender needed for export. + # The =[0] is a trick to emulate c-like static function variables + # that are persistant between calls. + def blender_export(self, level=[0]): + if self.object: + if options.verbose >= 2: + print '\t' * level[0], self.name, self.object.getType() + + level[0] += 1 + + for child in self.children: + child.blender_export() + + level[0] -= 1 + + # Exports this node's info to file. + def write(self): + pass + + def write_matrix(self): + if self.matrix and not is_identity(self.matrix): + self.header.fw.write_short(49) # Matrix opcode + self.header.fw.write_ushort(68) # Length of record + for i in xrange(4): + for j in xrange(4): + self.header.fw.write_float(self.matrix[i][j]) + + def write_push(self): + self.header.fw.write_short(10) + self.header.fw.write_ushort(4) + + def write_pop(self): + self.header.fw.write_short(11) + self.header.fw.write_ushort(4) + + def write_longid(self, name): + length = len(name) + if length >= 8: + self.header.fw.write_short(33) # Long ID opcode + self.header.fw.write_ushort(length+5) # Length of record + self.header.fw.write_string(name, length+1) # name + zero terminator + + # Initialization sets up basic tree structure. + def __init__(self, parent, header, object, object_lst): + self.header = header + self.object = object + if object: + self.name = self.object.getName() + self.matrix = self.object.getMatrix('localspace') + else: + self.name = 'no name' + self.matrix = None + + self.children = [] + self.parent = parent + if parent: + parent.children.append(self) + + left_over = object_lst[:] + self.child_objects = [] + + # Add children to child list and remove from left_over list. + + # Pop is faster then remove + i = len(object_lst) + while i: + i-=1 + if object_lst[i].parent == object: + self.child_objects.append(left_over.pop(i)) + + # Spawn children. + self.has_object_child = False # For Database class. + for child in self.child_objects: + if child.getType() == 'Mesh': + BlenderMesh(self, header, child, left_over) + self.has_object_child = True + else: # Treat all non meshes as emptys + BlenderEmpty(self, header, child, left_over) + +class FaceDesc: + def __init__(self): + self.vertex_index_lst = [] + self.texture_index = -1 + self.material_index = -1 + self.color_index = 127 + +class BlenderMesh(Node): + def blender_export(self): + Node.blender_export(self) + + mesh = self.object.getData() + mesh_hasuv = mesh.hasFaceUV() + # Gather materials and textures. + tex_index_lst = [] + mat_index_lst = [] + color_index_lst = [] + materials = mesh.getMaterials() + + if not materials: + materials = [Blender.Material.New()] + + for mat in materials: + # Gather Color. + if options.use_face_color: + color_index_lst.append(self.header.GRR.request_color_index(mat.getRGBCol())) + else: + color_index_lst.append(127) # white + # Gather Texture. + mtex_lst = mat.getTextures() + + index = -1 + mtex = mtex_lst[0] # Not doing multi-texturing at the moment. + if mtex != None: + tex = mtex_lst[0].tex + if tex != None: + image = tex.getImage() + if image != None: + filename = image.getFilename() + index = self.header.GRR.request_texture_index(filename) + + tex_index_lst.append(index) + + # Gather Material + mat_desc = MaterialDesc() + mat_desc.name = mat.getName() + mat_desc.alpha = mat.getAlpha() + mat_desc.shininess = mat.getSpec() * 64.0 # 2.0 => 128.0 + if options.use_mat_color: + mat_desc.diffuse = mat.getRGBCol() + else: + mat_desc.diffuse = [1.0, 1.0, 1.0] + + mat_desc.specular = mat.getSpecCol() + amb = mat.getAmb() + mat_desc.ambient = [amb, amb, amb] + emit = mat.getEmit() + mat_desc.emissive = [emit, emit, emit] + + mat_index_lst.append(self.header.GRR.request_material_index(mat_desc)) + + # Faces described as lists of indices into the GRR's vertex_lst. + for face in mesh.faces: + + face_v = face.v # Faster access + + # Create vertex description list for each face. + if mesh_hasuv: + vertex_lst = [VertexDesc(v.co, v.no, face.uv[i]) for i, v in enumerate(face_v)] + else: + vertex_lst = [VertexDesc(v.co, v.no) for i, v in enumerate(face_v)] + + index_lst = [] + for vert_desc in vertex_lst: + index_lst.append(self.header.GRR.request_vertex_index(vert_desc)) + + face_desc = FaceDesc() + face_desc.vertex_index_lst = index_lst + + if face.materialIndex < len(materials): + face_desc.color_index = color_index_lst[face.materialIndex] + face_desc.texture_index = tex_index_lst[face.materialIndex] + face_desc.material_index = mat_index_lst[face.materialIndex] + else: + if options.verbose >=1: + print 'Warning: Missing material for material index. Materials will not be imported correctly. Fix by deleting abandoned material indices in Blender.' + + self.face_lst.append(face_desc) + + # Export double sided face as 2 faces with opposite orientations. + if mesh_hasuv and face.mode & Blender.NMesh.FaceModes['TWOSIDE']: + # Create vertex description list for each face. they have a face mode, so we know they have a UV too. + vertex_lst = [VertexDesc(v.co, -v.no, face.uv[i]) for i, v in enumerate(face_v)] + vertex_lst.reverse() # Reversing flips the face. + + index_lst = [] + for vert_desc in vertex_lst: + index_lst.append(self.header.GRR.request_vertex_index(vert_desc)) + + face_desc = FaceDesc() + face_desc.vertex_index_lst = index_lst + if face.materialIndex < len(materials): + face_desc.color_index = color_index_lst[face.materialIndex] + face_desc.texture_index = tex_index_lst[face.materialIndex] + face_desc.material_index = mat_index_lst[face.materialIndex] + else: + if options.verbose >=1: + print 'Error: No material for material index. Delete abandoned material indices in Blender.' + + self.face_lst.append(face_desc) + + def write_faces(self): + for face_desc in self.face_lst: + face_name = self.header.GRR.new_face_name() + + self.header.fw.write_short(5) # Face opcode + self.header.fw.write_ushort(80) # Length of record + self.header.fw.write_string(face_name, 8) # ASCII ID + self.header.fw.write_int(-1) # IR color code + self.header.fw.write_short(0) # Relative priority + self.header.fw.write_char(0) # Draw type + self.header.fw.write_char(0) # Draw textured white. + self.header.fw.write_ushort(0) # Color name index + self.header.fw.write_ushort(0) # Alt color name index + self.header.fw.write_char(0) # Reserved + self.header.fw.write_char(1) # Template + self.header.fw.write_short(-1) # Detail tex pat index + self.header.fw.write_short(face_desc.texture_index) # Tex pattern index + self.header.fw.write_short(face_desc.material_index) # material index + self.header.fw.write_short(0) # SMC code + self.header.fw.write_short(0) # Feature code + self.header.fw.write_int(0) # IR material code + self.header.fw.write_ushort(0) # transparency 0 = opaque + self.header.fw.write_uchar(0) # LOD generation control + self.header.fw.write_uchar(0) # line style index + self.header.fw.write_int(0x00000000) # Flags + self.header.fw.write_uchar(2) # Light mode + self.header.fw.pad(7) # Reserved + self.header.fw.write_uint(-1) # Packed color + self.header.fw.write_uint(-1) # Packed alt color + self.header.fw.write_short(-1) # Tex map index + self.header.fw.write_short(0) # Reserved + self.header.fw.write_uint(face_desc.color_index) # Color index + self.header.fw.write_uint(127) # Alt color index + self.header.fw.write_short(0) # Reserved + self.header.fw.write_short(-1) # Shader index + + self.write_longid(face_name) + + self.write_push() + + # Vertex list record + self.header.fw.write_short(72) # Vertex list opcode + num_verts = len(face_desc.vertex_index_lst) + self.header.fw.write_ushort(4*num_verts+4) # Length of record + + for vert_index in face_desc.vertex_index_lst: + # Offset into vertex palette + self.header.fw.write_int(vert_index*64+8) + + self.write_pop() + + def write(self): + if self.open_flight_type == 'Object': + self.header.fw.write_short(4) # Object opcode + self.header.fw.write_ushort(28) # Length of record + self.header.fw.write_string(self.name, 8) # ASCII ID + self.header.fw.pad(16) + + self.write_longid(self.name) + + self.write_matrix() + + if self.face_lst != []: + self.write_push() + + self.write_faces() + + self.write_pop() + else: + self.header.fw.write_short(2) # Group opcode + self.header.fw.write_ushort(44) # Length of record + self.header.fw.write_string(self.name, 8) # ASCII ID + self.header.fw.pad(32) + + self.write_longid(self.name) + + # Because a group can contain faces as well as children. + self.write_push() + + self.write_faces() + + for child in self.children: + child.write() + + self.write_pop() + + def __init__(self, parent, header, object, object_lst): + Node.__init__(self, parent, header, object, object_lst) + self.face_lst = [] + + if self.children: + self.open_flight_type= 'Group' + else: # Empty list. + self.open_flight_type = 'Object' + + +class BlenderEmpty(Node): + def write(self): + self.header.fw.write_short(2) # Group opcode + self.header.fw.write_ushort(44) # Length of record + self.header.fw.write_string(self.name, 8) # ASCII ID + self.header.fw.pad(32) + + self.write_longid(self.name) + + self.write_matrix() + + if self.children: # != [] + self.write_push() + + for child in self.children: + child.write() + + self.write_pop() + +class Database(Node): + def write_header(self): + if options.verbose >= 2: + print 'Writing header.' + self.fw.write_short(1) # Header opcode + self.fw.write_ushort(324) # Length of record + self.fw.write_string('db', 8) # ASCII ID + self.fw.write_int(1600) # Revision Number + self.fw.pad(44) + self.fw.write_short(1) # Unit multiplier. + self.fw.write_char(0) # Units, 0 = meters + self.fw.write_char(0) # texwhite on new faces 0 = false + self.fw.write_uint(0x80000000) # misc flags set to saving vertex normals + self.fw.pad(24) + self.fw.write_int(0) # projection type, 0 = flat earth + self.fw.pad(30) + self.fw.write_short(1) # double precision + self.fw.pad(140) + self.fw.write_int(0) # ellipsoid model, 0 = WSG 1984 + self.fw.pad(52) + + def write_vert_pal(self): + if options.verbose >= 2: + print 'Writing vertex palette.' + # Write record for vertex palette + self.fw.write_short(67) # Vertex palette opcode. + self.fw.write_short(8) # Length of record + self.fw.write_int(self.GRR.vertex_count() * 64 + 8) # Length of everything. + + # Write records for individual vertices. + for i in xrange(self.GRR.vertex_count()): + desc = self.GRR.request_vertex_desc(i) + self.fw.write_short(70) # Vertex with color normal and uv opcode. + self.fw.write_ushort(64) # Length of record + self.fw.write_ushort(0) # Color name index + self.fw.write_short(0x2000) # Flags set to no color + self.fw.write_double(desc.x) + self.fw.write_double(desc.y) + self.fw.write_double(desc.z) + self.fw.write_float(desc.nx) + self.fw.write_float(desc.ny) + self.fw.write_float(desc.nz) + self.fw.write_float(desc.u) + self.fw.write_float(desc.v) + self.fw.pad(12) + + def write_tex_pal(self): + if options.verbose >= 2: + print 'Writing texture palette.' + # Write record for texture palette + for i in xrange(self.GRR.texture_count()): + self.fw.write_short(64) # Texture palette opcode. + self.fw.write_short(216) # Length of record + self.fw.write_string(self.GRR.request_texture_filename(i), 200) # Filename + self.fw.write_int(i) # Texture index + self.fw.write_int(0) # X + self.fw.write_int(0) # Y + + def write_mat_pal(self): + if options.verbose >= 2: + print 'Writing material palette.' + for i in xrange(self.GRR.material_count()): + desc = self.GRR.request_material_desc(i) + self.fw.write_short(113) # Material palette opcode. + self.fw.write_short(84) # Length of record + self.fw.write_int(i) # Material index + self.fw.write_string(desc.name, 12) # Material name + self.fw.write_uint(0x80000000) # Flags + self.fw.write_float(desc.ambient[0]) # Ambient color. + self.fw.write_float(desc.ambient[1]) # Ambient color. + self.fw.write_float(desc.ambient[2]) # Ambient color. + self.fw.write_float(desc.diffuse[0]) # Diffuse color. + self.fw.write_float(desc.diffuse[1]) # Diffuse color. + self.fw.write_float(desc.diffuse[2]) # Diffuse color. + self.fw.write_float(desc.specular[0]) # Specular color. + self.fw.write_float(desc.specular[1]) # Specular color. + self.fw.write_float(desc.specular[2]) # Specular color. + self.fw.write_float(desc.emissive[0]) # Emissive color. + self.fw.write_float(desc.emissive[1]) # Emissive color. + self.fw.write_float(desc.emissive[2]) # Emissive color. + self.fw.write_float(desc.shininess) + self.fw.write_float(desc.alpha) + self.fw.write_int(0) # Reserved + + def write_col_pal(self): + if options.verbose >= 2: + print 'Writing color palette.' + self.fw.write_short(32) # Color palette opcode. + self.fw.write_short(4228) # Length of record + self.fw.pad(128) + count = self.GRR.color_count() + for i in xrange(count): + col = self.GRR.request_max_color(i) + self.fw.write_uchar(255) # alpha + self.fw.write_uchar(col[2]) # b + self.fw.write_uchar(col[1]) # g + self.fw.write_uchar(col[0]) # r + self.fw.pad(max(4096-count*4, 0)) + + def write(self): + self.write_header() + self.write_vert_pal() + self.write_tex_pal() + self.write_mat_pal() + self.write_col_pal() + + # Wrap everything in a group if it has an object child. + if self.has_object_child: + self.header.fw.write_short(2) # Group opcode + self.header.fw.write_ushort(44) # Length of record + self.header.fw.write_string('g1', 8) # ASCII ID + self.header.fw.pad(32) + + self.write_push() + + for child in self.children: + child.write() + + self.write_pop() + + def __init__(self, scene, fw): + self.fw = fw + self.scene = scene + self.all_objects = scene.getChildren() + self.GRR = GlobalResourceRepository() + + Node.__init__(self, None, self, None, self.all_objects) + +def fs_callback(filename): + Blender.Window.WaitCursor(True) + + if Blender.sys.exists(filename): + r = Blender.Draw.PupMenu('Overwrite ' + filename + '?%t|Yes|No') + if r != 1: + if options.verbose >= 1: + print 'Export cancelled.' + return + + time1 = Blender.sys.time() # Start timing + + fw = FltOut(filename) + + db = Database(Blender.Scene.GetCurrent(), fw) + + if options.verbose >= 1: + print 'Pass 1: Exporting from Blender.\n' + + db.blender_export() + + if options.verbose >= 1: + print 'Pass 2: Writing %s\n' % filename + + db.write() + + fw.close_file() + if options.verbose >= 1: + print 'Done in %.4f sec.\n' % (Blender.sys.time() - time1) + + Blender.Window.WaitCursor(False) + +if options.verbose >= 1: + print '\nOpenFlight Exporter' + print 'Version:', __version__ + print 'Author: Greg MacDonald' + print __url__[2] + print + +fname = Blender.sys.makename(ext=".flt") +Blender.Window.FileSelector(fs_callback, "Export OpenFlight v16.0", fname) diff --git a/release/scripts/flt_filewalker.py b/release/scripts/flt_filewalker.py new file mode 100644 index 00000000000..f1f6ecd2847 --- /dev/null +++ b/release/scripts/flt_filewalker.py @@ -0,0 +1,278 @@ +#!BPY + +# flt_filewalker.py is an utility module for OpenFlight IO scripts for blender. +# Copyright (C) 2005 Greg MacDonald +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +import Blender +from struct import * +import re + +class FltIn: + def begin_record(self): + if self.repeat == True: + self.repeat = False + else: + self.position += self.length + try: + self.file.seek(self.position) + input = self.file.read(4) + except: + print 'Parse Error!' + return False + + if not input: + self.close_file() + return False + + self.opcode = unpack('>h', input[:2])[0] + self.length = unpack('>H', input[-2:])[0] + + self.next_position = self.position + self.length + + return True + + def repeat_record(self): + self.repeat = True + + def get_opcode(self): + return self.opcode + + def get_level(self): + return self.level + + def up_level(self): + self.level += 1 + + def down_level(self): + self.level -= 1 + + def read_string(self, length): + s = '' + if self.file.tell() + length <= self.next_position: + start = self.file.tell() + for i in range(length): + char = self.file.read(1) + if char == '\x00': + break + s = s + char + + self.file.seek(start+length) +# else: +# print 'Warning: string truncated' + + return s + + def read_int(self): + if self.file.tell() + 4 <= self.next_position: + return unpack('>i', self.file.read(4))[0] + else: + #print 'Warning: int truncated' + return 0 + + def read_uint(self): + if self.file.tell() + 4 <= self.next_position: + return unpack('>I', self.file.read(4))[0] + else: + #print 'Warning: uint truncated' + return 0 + + def read_double(self): + if self.file.tell() + 8 <= self.next_position: + return unpack('>d', self.file.read(8))[0] + else: + #print 'Warning: double truncated' + return 0.0 + + def read_float(self): + if self.file.tell() + 4 <= self.next_position: + return unpack('>f', self.file.read(4))[0] + else: + #print 'Warning: float truncated' + return 0.0 + + def read_ushort(self): + if self.file.tell() + 2 <= self.next_position: + return unpack('>H', self.file.read(2))[0] + else: + #print 'Warning: ushort truncated' + return 0 + + def read_short(self): + if self.file.tell() + 2 <= self.next_position: + return unpack('>h', self.file.read(2))[0] + else: + #print 'Warning: short trunated' + return 0 + + def read_uchar(self): + if self.file.tell() + 1 <= self.next_position: + return unpack('>B', self.file.read(1))[0] + else: + #print 'Warning: uchar truncated' + return 0 + + def read_char(self): + if self.file.tell() + 1 <= self.next_position: + return unpack('>b', self.file.read(1))[0] + else: + #print 'Warning: char truncated' + return 0 + + def read_ahead(self, i): + if self.file.tell() + i <= self.next_position: + self.file.seek(i, 1) +# else: +# print 'Warning: attempt to seek past record' + + def get_length(self): + return self.length + + def close_file(self): + self.file.close() + + def __init__(self, filename): + self.file = open(filename, 'rb') + self.position = 0 + self.next_position = 100000 + self.opcode = 0 + self.length = 0 + self.level = 0 + self.repeat = False # Repeat the last record. + +class FltOut: + # Length includes terminating null + def write_string(self, string, length): + if len(string) > length - 1: + str_len = length - 1 + else: + str_len = len(string) + + pad_len = length - str_len + + self.file.write(string[:str_len]) + + self.pad(pad_len) + + def write_int(self, a): + self.file.write( pack('>i', a) ) + + def write_uint(self, a): + self.file.write( pack('>I', a) ) + + def write_double(self, a): + self.file.write( pack('>d', a) ) + + def write_float(self, a): + self.file.write( pack('>f', a) ) + + def write_ushort(self, a): + self.file.write( pack('>H', a) ) + + def write_short(self, a): + self.file.write( pack('>h', a) ) + + def write_uchar(self, a): + self.file.write( pack('>B', a) ) + + def write_char(self, a): + self.file.write( pack('>b', a) ) + + def pad(self, reps): + for i in range(reps): + self.file.write('\x00') + + def close_file(self): + self.file.close() + + def __init__(self, filename): + self.file = open(filename, 'wb') + +class FileFinder: + def add_file_to_search_path(self, filename): + dir = Blender.sys.dirname(filename) + if dir != None and dir != '': + self.search_dirs.append(dir) + + def strip_path(self, full_path): + # One of my flt files had a windows path with unix seperation. Basename + # returned the whole path + filename, which isn't expected. So my + # attempt to fix it is to replace all / or \ with the platform specific + # dir seperator. + # + # note: \\\\ is actually just one \ indirected twice, once for python + # then again for re.sub + if Blender.sys.sep == '\\': + full_path = re.sub('/', '\\\\', full_path) + elif Blender.sys.sep == '/': + full_path = re.sub('\\\\', '/', full_path) + + filename = Blender.sys.basename(full_path) + return filename + + def find(self, full_path): + if full_path == '': + return None + + # Seperate out the path. + dirname = Blender.sys.dirname(full_path) + + # Try it first. + if Blender.sys.exists(full_path): + if not dirname in self.search_dirs: + self.search_dirs.append(dirname) + return full_path + + # Maybe it's relative. + for path in self.search_dirs: + rel_full_path = Blender.sys.join(path, full_path) + if Blender.sys.exists(rel_full_path): + return rel_full_path + + # Search previous directories that have worked. + filename = self.strip_path(full_path) + for path in self.search_dirs: + t = Blender.sys.join(path, filename) + if Blender.sys.exists(t): + return t + + # Ask user where it is. + self.user_input = Blender.Draw.PupStrInput(filename + "? ", '', 100) + if self.user_input != None: + t = Blender.sys.join(self.user_input, filename) + if Blender.sys.exists(t): + user_dirname = Blender.sys.dirname(t) + if not user_dirname in self.search_dirs: + self.search_dirs.append(user_dirname) + return t + + # Couldn't find it. + return None + + def __init__(self): + self.user_input = '' + self.current_file = '' + self.search_dirs = [] + + dir = Blender.Get('texturesdir') + if dir != None and dir != '': + self.search_dirs.append(dir) + + dir = Blender.sys.dirname(Blender.Get('filename')) + if dir != None and dir != '': + print dir + self.search_dirs.append(dir) +
\ No newline at end of file diff --git a/release/scripts/flt_import.py b/release/scripts/flt_import.py new file mode 100755 index 00000000000..2553f34dbf0 --- /dev/null +++ b/release/scripts/flt_import.py @@ -0,0 +1,1803 @@ +#!BPY + +# flt_import.py is an OpenFlight importer for blender. +# Copyright (C) 2005 Greg MacDonald +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +""" Registration info for Blender menus: +Name: 'OpenFlight (.flt)...' +Blender: 238 +Group: 'Import' +Tip: 'Import OpenFlight (.flt)' +""" + +__author__ = "Greg MacDonald" +__version__ = "1.2 10/20/05" +__url__ = ("blender", "elysiun", "Author's homepage, http://sourceforge.net/projects/blight/") +__bpydoc__ = """\ +This script imports OpenFlight files into Blender. OpenFlight is a +registered trademark of MultiGen-Paradigm, Inc. + +Run from "File->Import" menu. + +Options are available from Blender's "Scripts Config Editor," accessible through +the "Scripts->System" menu from the scripts window. + +All options are toggle switches that let the user choose what is imported. Most +are straight-forward, but one option could be a source of confusion. The +"Diffuse Color From Face" option when set pulls the diffuse color from the face +colors. Otherwise the diffuse color comes from the material. What may be +confusing is that this options only works if the "Diffuse Color" option is set. + +New Features:<br> +* Importer is 14 times faster.<br> +* External triangle module is no longer required, but make sure the importer +has a 3d View screen open while its running or triangulation won't work.<br> +* Should be able to import all versions of flight files. + +Features:<br> +* Heirarchy retained.<br> +* First texture imported.<br> +* Colors imported from face or material.<br> +* LOD seperated out into different layers.<br> +* Asks for location of unfound textures or external references.<br> +* Searches Blender's texture directory in the user preferences panel.<br> +* Triangles with more than 4 verts are triangulated if the Triangle python +module is installed.<br> +* Matrix transforms imported.<br> +* External references to whole files are imported. + +Things To Be Aware Of:<br> +* Each new color and face attribute creates a new material and there are only a maximum of 16 +materials per object.<br> +* For triangulated faces, normals must be recomputed outward manually by typing +CTRL+N in edit mode.<br> +* You can change options only after an initial import.<br> +* External references are imported as geometry and will be exported that way.<br> +* A work around for not using the Triangle python module is to simply to +triangulate in Creator before importing. This is only necessary if your +model contains 5 or more vertices.<br> +* You have to manually blend the material color with the texture color. + +What's Not Handled:<br> +* Special texture repeating modes.<br> +* Replications and instancing.<br> +* Comment and attribute fields.<br> +* Light points.<br> +* Animations.<br> +* External references to a node within a file.<br> +* Multitexturing.<br> +* Vetex colors.<br> +""" + +import Blender +import os + +from flt_filewalker import FltIn, FileFinder + +def col_to_gray(c): + return 0.3*c[0] + 0.59*c[1] + 0.11*c[2] + +class ImporterOptions: + def __init__(self): + self.defaults = { 'Texture': True, + 'Diffuse Color': True, + 'Specular Color': False, + 'Emissive Intensity': False, + 'Alpha': True, + 'Ambient Intensity': False, + 'Shininess': True, + 'Diffuse Color From Face': True} + + d = Blender.Registry.GetKey('flt_import', True) + + if d == None or d.keys() != self.defaults.keys(): + d = self.defaults + Blender.Registry.SetKey('flt_import', d, True) + + self.verbose = 1 + self.get_texture = d['Texture'] + self.get_diffuse = d['Diffuse Color'] + self.get_specular = d['Specular Color'] + self.get_emissive = d['Emissive Intensity'] + self.get_alpha = d['Alpha'] + self.get_ambient = d['Ambient Intensity'] + self.get_shininess = d['Shininess'] + self.color_from_face = d['Diffuse Color From Face'] + +options = ImporterOptions() + +# Contributed by Campbell Barton: +# http://en.wikibooks.org/wiki/Blending_Into_Python:_Cookbook#Expanded_Scanfill_function +msg_once = False +def scanFillPoints(pointList): + global msg_once + + screen_info = Blender.Window.GetScreenInfo(Blender.Window.Types.VIEW3D) + if screen_info == []: + if not msg_once: + msg = 'Error: A 3D View window must be open while the script is running for triangulation to occur.' + Blender.Draw.PupMenu(msg) + print msg + print + msg_once = True + return None + + Blender.Window.EditMode(0) + + nme = Blender.NMesh.GetRaw('.scanfill') + if nme: + nme.verts = [] + nme.edges = [] + nme. faces = [] + else: + nme = Blender.NMesh.New('.scanfill') + + for p in pointList: + v = Blender.NMesh.Vert( p[0], p[1], p[2] ) + nme.verts.append(v) + v.sel = 1 + + if len(nme.verts) >= 2: + nme.addEdge(nme.verts[-2], nme.verts[-1]) + + nme.addEdge(nme.verts[0], nme.verts[-1]) + + nme.update() + + scn = Blender.Scene.GetCurrent() + + actOb = scn.getActiveObject() + if actOb: + actSel = actOb.sel + else: + actSel = 0 + + try: + ob = Blender.Object.Get('.scanfill') + except AttributeError: + ob = Blender.Object.New('Mesh') + ob.setName('.scanfill') + ob.link(nme) + + scn.link(ob) + scn.layers = range(1,20) + ob.sel = 1 + Blender.Window.EditMode(1) + + winid = screen_info[0]['id'] + Blender.Window.SetKeyQualifiers(Blender.Window.Qual.SHIFT) + Blender.Window.QAdd(winid, Blender.Draw.FKEY,1) + Blender.Window.QHandle(winid) + Blender.Window.SetKeyQualifiers(0) + + Blender.Window.EditMode(0) + scn.unlink(ob) + + # Select the old active object. + if actOb: + actOb.sel = actSel + + # Return the scanfilled faces. + return ob.getData() + +class MaterialDesc: + # Was going to use int(f*1000.0) instead of round(f,3), but for some reason + # round produces better results, as in less dups. + def make_key(self): + key = [] + if options.get_texture: + if self.tex0: + key.append(self.tex0.getName()) + else: + key.append(None) + + if options.get_alpha: + key.append(round(self.alpha, 3)) + else: + key.append(None) + + if options.get_shininess: + key.append(round(self.shininess, 3)) + else: + key.append(None) + + if options.get_emissive: + key.append(round(self.emissive, 3)) + else: + key.append(None) + + if options.get_ambient: + key.append(round(self.ambient, 3)) + else: + key.append(None) + + if options.get_specular: + for n in self.specular: + key.append(round(n, 3)) + else: + key.extend([None, None, None]) + + if options.get_diffuse: + for n in self.diffuse: + key.append(round(n, 3)) + else: + key.extend([None, None, None]) + +# key.extend(self.face_props.values()) + + return tuple(key) + + def __init__(self): + self.name = 'Material' + # Colors, List of 3 floats. + self.diffuse = [1.0, 1.0, 1.0] + self.specular = [1.0, 1.0, 1.0] + + # Scalars + self.ambient = 0.0 # [0.0, 1.0] + self.emissive = 0.0 # [0.0, 1.0] + self.shininess = 0.5 # Range is [0.0, 2.0] + self.alpha = 1.0 # Range is [0.0, 1.0] + + self.tex0 = None + + # OpenFlight Face attributes + self.face_props = dict.fromkeys(['comment', 'ir color', 'priority', + 'draw type', 'texture white', 'template billboard', + 'smc', 'fid', 'ir material', 'lod generation control', + 'flags', 'light mode']) + +class VertexDesc: + def make_key(self): + return (round(self.x,3), round(self.y, 3), round(self.z, 3)) + + def __init__(self): + self.x = 0.0 + self.y = 0.0 + self.z = 0.0 + self.nx = 0.0 + self.ny = 1.0 + self.nz = 0.0 + self.u = 0.0 + self.v = 0.0 + self.r = 1.0 + self.g = 1.0 + self.b = 1.0 + self.a = 1.0 + +class LightPointAppDesc: + def make_key(self): + d = dict(self.props) + del d['id'] + del d['type'] + + if d['directionality'] != 0: # not omni + d['nx'] = 0.0 + d['ny'] = 0.0 + d['nz'] = 0.0 + + return tuple(d.values()) + + def __init__(self): + self.props = dict() + self.props.update({'type': 'LPA'}) + self.props.update({'id': 'ap'}) + # Attribs not found in inline lightpoint. + self.props.update({'visibility range': 0.0}) + self.props.update({'fade range ratio': 0.0}) + self.props.update({'fade in duration': 0.0}) + self.props.update({'fade out duration': 0.0}) + self.props.update({'LOD range ratio': 0.0}) + self.props.update({'LOD scale': 0.0}) + +class GlobalResourceRepository: + def request_lightpoint_app(self, desc): + match = self.light_point_app.get(desc.make_key()) + + if match: + return match.getName() + else: + # Create empty and fill with properties. + name = desc.props['type'] + ': ' + desc.props['id'] + object = Blender.Object.New('Empty', name) + + scene.link(object) + + # Attach properties + for name, value in desc.props.items(): + object.addProperty(name, value) + + self.light_point_app.update({desc.make_key(): object}) + + return object.getName() + + def request_vert(self, desc): + match = self.vert_dict.get(desc.make_key()) + + if match: + return match + else: + vert = Blender.NMesh.Vert(desc.x, desc.y, desc.z) + + vert.no[0] = desc.nx + vert.no[1] = desc.ny + vert.no[2] = desc.nz + + self.vert_dict.update({desc.make_key(): vert}) + return vert + + def request_mat(self, mat_desc): + match = self.mat_dict.get(mat_desc.make_key()) + if match: return match + + mat = Blender.Material.New(mat_desc.name) + + if mat_desc.tex0 != None: + mat.setTexture(0, mat_desc.tex0, Blender.Texture.TexCo.UV) + + mat.setAlpha(mat_desc.alpha) + mat.setSpec(mat_desc.shininess) + mat.setHardness(255) + mat.setEmit(mat_desc.emissive) + mat.setAmb(mat_desc.ambient) + mat.setSpecCol(mat_desc.specular) + mat.setRGBCol(mat_desc.diffuse) + + # Create a text object to store openflight face attribs until + # user properties can be set on materials. +# t = Blender.Text.New('FACE: ' + mat.getName()) +# +# for name, value in mat_desc.face_props.items(): +# t.write(name + '\n' + str(value) + '\n\n') + + self.mat_dict.update({mat_desc.make_key(): mat}) + + return mat + + def request_image(self, filename_with_path): + if not options.get_texture: return None + + img = self.img_dict.get(filename_with_path) + if img: return img + + img = Blender.Image.Load(filename_with_path) + self.img_dict.update({filename_with_path: img}) + return img + + def request_texture(self, image): + if not options.get_texture: + return None + + tex = self.tex_dict.get(image.filename) + if tex: return tex + + tex = Blender.Texture.New(Blender.sys.basename(image.filename)) + tex.setImage(image) + tex.setType('Image') + self.tex_dict.update({image.filename: tex}) + return tex + + def __init__(self): + # material + self.mat_dict = dict() + mat_lst = Blender.Material.Get() + for mat in mat_lst: + mat_desc = MaterialDesc() + mapto_lst = mat.getTextures() + if mapto_lst[0]: + mat_desc.tex0 = mapto_lst[0].tex + else: + mat_desc.tex0 = None + mat_desc.alpha = mat.getAlpha() + mat_desc.shininess = mat.getSpec() + mat_desc.emissive = mat.getEmit() + mat_desc.ambient = mat.getAmb() + mat_desc.specular = mat.getSpecCol() + mat_desc.diffuse = mat.getRGBCol() + + self.mat_dict.update({mat_desc.make_key(): mat}) + + # texture + self.tex_dict = dict() + tex_lst = Blender.Texture.Get() + + for tex in tex_lst: + img = tex.getImage() + # Only interested in textures with images. + if img: + self.tex_dict.update({img.filename: tex}) + + # image + img_lst = Blender.Image.Get() + self.img_dict = dict() + for img in img_lst: + self.img_dict.update({img.filename: img}) + + # vertex + self.vert_dict = dict() + + # light point + self.light_point_app = dict() + +# Globals +GRR = GlobalResourceRepository() +FF = FileFinder() +current_layer = 0x1 +scene = Blender.Scene.getCurrent() + +# Opcodes that indicate its time to return control to parent. +throw_back_opcodes = [2, 73, 4, 11, 96, 14, 91, 98, 63] +do_not_report_opcodes = [76, 78, 79, 80, 81, 82, 94, 83, 33, 112, 100, 101, 102, 97, 31, 103, 104, 117, 118, 120, 121, 124, 125] + +opcode_name = { 0: 'db', + 1: 'head', + 2: 'grp', + 4: 'obj', + 5: 'face', + 10: 'push', + 11: 'pop', + 14: 'dof', + 19: 'push sub', + 20: 'pop sub', + 21: 'push ext', + 22: 'pop ext', + 23: 'cont', + 31: 'comment', + 32: 'color pal', + 33: 'long id', + 49: 'matrix', + 50: 'vector', + 52: 'multi-tex', + 53: 'uv lst', + 55: 'bsp', + 60: 'rep', + 61: 'inst ref', + 62: 'inst def', + 63: 'ext ref', + 64: 'tex pal', + 67: 'vert pal', + 68: 'vert w col', + 69: 'vert w col & norm', + 70: 'vert w col, norm & uv', + 71: 'vert w col & uv', + 72: 'vert lst', + 73: 'lod', + 74: 'bndin box', + 76: 'rot edge', + 78: 'trans', + 79: 'scl', + 80: 'rot pnt', + 81: 'rot and/or scale pnt', + 82: 'put', + 83: 'eyepoint & trackplane pal', + 84: 'mesh', + 85: 'local vert pool', + 86: 'mesh prim', + 87: 'road seg', + 88: 'road zone', + 89: 'morph vert lst', + 90: 'link pal', + 91: 'snd', + 92: 'rd path', + 93: 'snd pal', + 94: 'gen matrix', + 95: 'txt', + 96: 'sw', + 97: 'line styl pal', + 98: 'clip reg', + 100: 'ext', + 101: 'light src', + 102: 'light src pal', + 103: 'reserved', + 104: 'reserved', + 105: 'bndin sph', + 106: 'bndin cyl', + 107: 'bndin hull', + 108: 'bndin vol cntr', + 109: 'bndin vol orient', + 110: 'rsrvd', + 111: 'light pnt', + 112: 'tex map pal', + 113: 'mat pal', + 114: 'name tab', + 115: 'cat', + 116: 'cat dat', + 117: 'rsrvd', + 118: 'rsrvd', + 119: 'bounding hist', + 120: 'rsrvd', + 121: 'rsrvd', + 122: 'push attrib', + 123: 'pop attrib', + 124: 'rsrvd', + 125: 'rsrvd', + 126: 'curv', + 127: 'road const', + 128: 'light pnt appear pal', + 129: 'light pnt anim pal', + 130: 'indexed lp', + 131: 'lp sys', + 132: 'indx str', + 133: 'shdr pal'} + +class Handler: + def in_throw_back_lst(self, opcode): + return opcode in self.throw_back_lst + + def handle(self, opcode): + return self.handler[opcode]() + + def handles(self, opcode): + return opcode in self.handler.keys() + + def throws_back_all_unhandled(self): + return self.throw_back_unhandled + + def set_throw_back_lst(self, a): + self.throw_back_lst = a + + def set_throw_back_all_unhandled(self): + self.throw_back_unhandled = True + + def set_only_throw_back_specified(self): + self.throw_back_unhandled = False + + def set_handler(self, d): + self.handler = d + + def __init__(self): + # Dictionary of opcodes to handler methods. + self.handler = dict() + # Send all opcodes not handled to the parent node. + self.throw_back_unhandled = False + # If throw_back_unhandled is False then only throw back + # if the opcodes in throw_back are encountered. + self.throw_back_lst = [] + +class Node: + def blender_import(self): + if self.opcode in opcode_name and options.verbose >= 2: + for i in range(self.get_level()): + print ' ', + print opcode_name[self.opcode], + print '-', self.props['id'], + print '-', self.props['comment'], + + print + + for child in self.children: + child.blender_import() + + # Import comment. +# if self.props['comment'] != '': +# name = 'COMMENT: ' + self.props['id'] +# t = Blender.Text.New(name) +# t.write(self.props['comment']) +# self.props['comment'] = name + + # Always ignore extensions and anything in between them. + def parse_push_extension(self): + self.saved_handler = self.active_handler + self.active_handler = self.extension_handler + return True + + def parse_pop_extension(self): + self.active_handler = self.saved_handler + return True + + def parse_push(self): + self.header.fw.up_level() + # Ignore unknown children. + self.ignore_unhandled = True + # Don't do child records that might overwrite parent info. ex: longid + self.active_handler = self.child_handler + return True + + def parse_pop(self): + self.header.fw.down_level() + + if self.header.fw.get_level() == self.level: + return False + + return True + + def parse(self): + while self.header.fw.begin_record(): + opcode = self.header.fw.get_opcode() + + # Print out info on opcode and tree level. + if options.verbose >= 3: + p = '' + for i in range(self.header.fw.get_level()): + p = p + ' ' + if opcode in opcode_name: + p = p + opcode_name[opcode] + else: + if options.verbose >= 1: + print 'undocumented opcode', opcode + continue + + if self.global_handler.handles(opcode): + if options.verbose >= 3: + print p + ' handled globally' + if self.global_handler.handle(opcode) == False: + break + + elif self.active_handler.handles(opcode): + if options.verbose >= 4: + print p + ' handled' + if self.active_handler.handle(opcode) == False: + break + + else: + if self.active_handler.throws_back_all_unhandled(): + if options.verbose >= 3: + print p + ' handled elsewhere' + self.header.fw.repeat_record() + break + + elif self.active_handler.in_throw_back_lst(opcode): + if options.verbose >= 3: + print p + ' handled elsewhere' + self.header.fw.repeat_record() + break + + else: + if options.verbose >= 3: + print p + ' ignored' + elif options.verbose >= 1 and not opcode in do_not_report_opcodes and opcode in opcode_name: + print opcode_name[opcode], 'not handled' + + def get_level(self): + return self.level + + def parse_long_id(self): + self.props['id'] = self.header.fw.read_string(self.header.fw.get_length()-4) + return True + + def parse_comment(self): + self.props['comment'] = self.header.fw.read_string(self.header.fw.get_length()-4) + return True + + def __init__(self, parent, header): + self.root_handler = Handler() + self.child_handler = Handler() + self.extension_handler = Handler() + self.global_handler = Handler() + + self.global_handler.set_handler({21: self.parse_push_extension}) + self.active_handler = self.root_handler + + # used by parse_*_extension + self.extension_handler.set_handler({22: self.parse_pop_extension}) + self.saved_handler = None + + self.header = header + self.children = [] + + self.parent = parent + + if parent: + parent.children.append(self) + + self.level = self.header.fw.get_level() + self.opcode = self.header.fw.get_opcode() + + self.props = {'id': 'unnamed', 'comment': '', 'type': 'untyped'} + +class VertexPalette(Node): + def blender_import(self): + for vert_desc in self.vert_desc_lst: + vert = GRR.request_vert(vert_desc) + self.blender_verts.append(vert) + + def parse_vertex_common(self): + # Add this vertex to an offset to index dictionary. + self.index_lst.append( (self.offset, self.next_index) ) + self.next_index += 1 + # Get ready for next record. + self.offset += self.header.fw.get_length() + + v = VertexDesc() + + self.header.fw.read_ahead(2) + v.flags = self.header.fw.read_short() + + v.x = self.header.fw.read_double() + v.y = self.header.fw.read_double() + v.z = self.header.fw.read_double() + + return v + + def parse_vertex_post_common(self, v): + if not v.flags & 0x2000: # 0x2000 = no color + if v.flags & 0x1000: # 0x1000 = packed color + v.a = self.header.fw.read_uchar() + v.b = self.header.fw.read_uchar() + v.g = self.header.fw.read_uchar() + v.r = self.header.fw.read_uchar() + else: + self.header.fw.read_ahead(4) + + color_index = self.header.fw.read_uint() + color = self.header.get_color(color_index) + v.r = color[0] + v.g = color[1] + v.b = color[2] + v.a = color[3] + + self.vert_desc_lst.append(v) + + return True + + def parse_vertex_c(self): + v = self.parse_vertex_common() + + self.parse_vertex_post_common(v) + + return True + + def parse_vertex_cn(self): + v = self.parse_vertex_common() + + v.nx = self.header.fw.read_float() + v.ny = self.header.fw.read_float() + v.nz = self.header.fw.read_float() + + self.parse_vertex_post_common(v) + + return True + + def parse_vertex_cuv(self): + v = self.parse_vertex_common() + + v.u = self.header.fw.read_float() + v.v = self.header.fw.read_float() + + self.parse_vertex_post_common(v) + + return True + + def parse_vertex_cnuv(self): + v = self.parse_vertex_common() + + v.nx = self.header.fw.read_float() + v.ny = self.header.fw.read_float() + v.nz = self.header.fw.read_float() + + v.u = self.header.fw.read_float() + v.v = self.header.fw.read_float() + + self.parse_vertex_post_common(v) + + return True + + def parse(self): + Node.parse(self) + + self.index = dict(self.index_lst) + del self.index_lst + + def __init__(self, parent): + Node.__init__(self, parent, parent.header) + self.root_handler.set_handler({68: self.parse_vertex_c, + 69: self.parse_vertex_cn, + 70: self.parse_vertex_cnuv, + 71: self.parse_vertex_cuv}) + self.root_handler.set_throw_back_all_unhandled() + + self.vert_desc_lst = [] + self.blender_verts = [] + self.offset = 8 + # Used to create a map from byte offset to vertex index. + self.index = dict() + self.index_lst = [] + self.next_index = 0 + +class InterNode(Node): + def blender_import(self): +# name = self.props['type'] + ': ' + self.props['id'] + name = self.props['id'] + if self.isMesh: + self.object = Blender.Object.New('Mesh', name) + self.mesh = self.object.getData() + else: + self.object = Blender.Object.New('Empty', name) + + if self.parent: + self.parent.object.makeParent([self.object]) + + scene.link(self.object) + + self.object.Layer = current_layer + + Node.blender_import(self) + + if self.isMesh: + self.mesh.update(recalc_normals=1) + + if self.matrix: + self.object.setMatrix(self.matrix) + + # Attach properties + #for name, value in self.props.items(): + # self.object.addProperty(name, value) + + def parse_face(self): + child = Face(self) + child.parse() + return True + + def parse_group(self): + child = Group(self) + child.parse() + return True + + def move_to_next_layer(self): + global current_layer + current_layer = current_layer << 1 + if current_layer > 0x80000: + current_layer = 1 + + def parse_lod(self): + child = LOD(self) + child.parse() + return True + + def parse_unhandled(self): + child = Unhandled(self) + child.parse() + return True + + def parse_object(self): + child = Object(self) + child.parse() + return True + + def parse_xref(self): + child = XRef(self) + child.parse() + return True + + def parse_indexed_light_point(self): + child = IndexedLightPoint(self) + child.parse() + return True + + def parse_inline_light_point(self): + child = InlineLightPoint(self) + child.parse() + return True + + def parse_matrix(self): + m = [] + for i in range(4): + m.append([]) + for j in range(4): + f = self.header.fw.read_float() + m[i].append(f) + self.matrix = Blender.Mathutils.Matrix(m[0], m[1], m[2], m[3]) + + def __init__(self): + self.object = None + self.mesh = None + self.isMesh = False + self.matrix = None + +class Face(Node): + def blender_import_face(self, indices, material_index, image): + mesh = self.parent.mesh + face = Blender.NMesh.Face() + + # Add vertices to face. + for i in indices: + # Add uv info to face. + vert_desc = self.header.vert_pal.vert_desc_lst[i] + if vert_desc.u != None and vert_desc.v != None: + mesh.hasFaceUV(True) + face.uv.append((vert_desc.u, vert_desc.v)) + + # Add vert to face. + vert = self.header.vert_pal.blender_verts[i] + face.v.append(vert) + + # Add vert to mesh. + if not vert in mesh.verts: + mesh.verts.append(vert) + + if image: + face.image = image + face.materialIndex = material_index + face.smooth = True + + mesh.addFace(face) + + def parse_comment(self): + self.comment = self.header.fw.read_string(self.header.fw.get_length()-4) + return True + + # returns a tuple (material, image) where material is the blender material and + # image is the blender image or None. + def create_blender_material(self): + # Create face material. + mat_desc = MaterialDesc() + + if self.mat_index != -1: + if not self.mat_index in self.header.mat_desc_pal: + if options.verbose >= 1: + print 'Warning: Material index', self.mat_index, 'not in material palette.' + else: + mat_pal_desc = self.header.mat_desc_pal[self.mat_index] + mat_desc.alpha = mat_pal_desc.alpha * self.alpha # combine face and mat alphas + mat_desc.ambient = mat_pal_desc.ambient + mat_desc.diffuse = mat_pal_desc.diffuse + mat_desc.specular = mat_pal_desc.specular + mat_desc.emissive = mat_pal_desc.emissive + mat_desc.shininess = mat_pal_desc.shininess + else: + # if no material get alpha from just face. + mat_desc.alpha = self.alpha + + # Color. + if options.color_from_face: + color = None + if not self.props['flags'] & 0x40000000: + if self.props['flags'] & 0x10000000: # packed color + color = self.packed_color + else: + color = self.header.get_color(self.color_index) + + if color: + r = float(color[0])/255.0 + g = float(color[1])/255.0 + b = float(color[2])/255.0 + mat_desc.diffuse = [r, g, b] + + # Texture + image = None + if self.tex_index != -1 and self.tex_index in self.header.bl_tex_pal: + mat_desc.tex0 = self.header.bl_tex_pal[self.tex_index] + if mat_desc.tex0: + mat_desc.name = FF.strip_path(self.header.tex_pal[self.tex_index]) + image = mat_desc.tex0.image + + # OpenFlight Face Attributes + mat_desc.face_props = self.props + + # Get material. + mat = GRR.request_mat(mat_desc) + + # Add material to mesh. + mesh = self.parent.mesh + try: + mesh.addMaterial(mat) + except AttributeError: + pass + except RuntimeError: + if options.verbose >= 1: + print 'Warning: Too many materials per mesh object. Only a maximum of 16 ' + \ + 'allowed. Using 16th material instead.' + mat = mesh.materials[-1] + + # Return where it is in the mesh for faces. + material_index = mesh.materials.index(mat) + + return (material_index, image) + + def triangulate(self): + point_lst = [] + for i in self.indices: + vert_desc = self.header.vert_pal.vert_desc_lst[i] + point_lst.append(self.header.vert_pal.blender_verts[i].co) + + mesh = scanFillPoints(point_lst) + if not mesh: + return [] + + # mesh.verts and vert_lst should be in the same order unless blender rearranged them during triangulation, unlikely. + tri_lst = [] + for f in mesh.faces: + tri = [] + for vert in f.v: + i = mesh.verts.index(vert) + tri.append(self.indices[i]) + tri_lst.append(tri) + + return tri_lst + + def blender_import(self): + vert_count = len(self.indices) + if vert_count == 0: + if options.verbose >= 2: + print 'Warning: Ignoring face with no vertices.' + return + + material = self.create_blender_material() + + if vert_count > 4: + tri_lst = self.triangulate() + else: + tri_lst = [self.indices] + + for tri in tri_lst: + self.blender_import_face(tri, material[0], material[1]) + + # Store comment info in parent. + if self.comment != '': + if self.parent.props['comment'] != '': + self.parent.props['comment'] += '\n\nFrom Face:\n' + self.comment + else: + self.parent.props['comment'] = self.comment + + def parse_vertex_list(self): + length = self.header.fw.get_length() + fw = self.header.fw + vert_pal = self.header.vert_pal + + count = (length-4)/4 + + for i in range(count): + byte_offset = fw.read_int() + if byte_offset in vert_pal.index: + index = vert_pal.index[byte_offset] + self.indices.append(index) + elif options.verbose >= 1: + print 'Warning: Unable to map byte offset %s' + \ + ' to vertex index.' % byte_offset + + return True + + def __init__(self, parent): + Node.__init__(self, parent, parent.header) + self.root_handler.set_handler({31: self.parse_comment, + 10: self.parse_push}) + self.root_handler.set_throw_back_lst(throw_back_opcodes) + + self.child_handler.set_handler({72: self.parse_vertex_list, + 10: self.parse_push, + 11: self.parse_pop}) + + if parent: + parent.isMesh = True + + self.indices = [] + + self.comment = '' + self.props = dict.fromkeys(['ir color', 'priority', + 'draw type', 'texture white', 'template billboard', + 'smc', 'fid', 'ir material', 'lod generation control', + 'flags', 'light mode']) + + self.header.fw.read_ahead(8) # face id + # Load face. + self.props['ir color'] = self.header.fw.read_int() + self.props['priority'] = self.header.fw.read_short() + self.props['draw type'] = self.header.fw.read_char() + self.props['texture white'] = self.header.fw.read_char() + self.header.fw.read_ahead(4) # color name indices + self.header.fw.read_ahead(1) # reserved + self.props['template billboard'] = self.header.fw.read_uchar() + self.detail_tex_index = self.header.fw.read_short() + self.tex_index = self.header.fw.read_short() + self.mat_index = self.header.fw.read_short() + self.props['smc'] = self.header.fw.read_short() + self.props['fid'] = self.header.fw.read_short() + self.props['ir material'] = self.header.fw.read_int() + self.alpha = 1.0 - float(self.header.fw.read_ushort()) / 65535.0 + self.props['lod generation control'] = self.header.fw.read_uchar() + self.header.fw.read_ahead(1) # line style index + self.props['flags'] = self.header.fw.read_int() + self.props['light mode'] = self.header.fw.read_uchar() + self.header.fw.read_ahead(7) + a = self.header.fw.read_uchar() + b = self.header.fw.read_uchar() + g = self.header.fw.read_uchar() + r = self.header.fw.read_uchar() + self.packed_color = [r, g, b, a] + a = self.header.fw.read_uchar() + b = self.header.fw.read_uchar() + g = self.header.fw.read_uchar() + r = self.header.fw.read_uchar() + self.alt_packed_color = [r, g, b, a] + self.tex_map_index = self.header.fw.read_short() + self.header.fw.read_ahead(2) + self.color_index = self.header.fw.read_uint() + self.alt_color_index = self.header.fw.read_uint() + #self.header.fw.read_ahead(2) + #self.shader_index = self.header.fw.read_short() + +class Object(InterNode): + def __init__(self, parent): + Node.__init__(self, parent, parent.header) + InterNode.__init__(self) + + self.root_handler.set_handler({33: self.parse_long_id, + 31: self.parse_comment, + 10: self.parse_push, + 49: self.parse_matrix}) + self.root_handler.set_throw_back_lst(throw_back_opcodes) + + self.child_handler.set_handler({5: self.parse_face, + #130: self.parse_indexed_light_point, + #111: self.parse_inline_light_point, + 10: self.parse_push, + 11: self.parse_pop}) + + self.props['type'] = 'Object' + self.props['id'] = self.header.fw.read_string(8) + +class Group(InterNode): + def __init__(self, parent): + Node.__init__(self, parent, parent.header) + InterNode.__init__(self) + + self.root_handler.set_handler({33: self.parse_long_id, + 31: self.parse_comment, + 10: self.parse_push, + 49: self.parse_matrix}) + self.root_handler.set_throw_back_lst(throw_back_opcodes) + + self.child_handler.set_handler({5: self.parse_face, + #130: self.parse_indexed_light_point, + #111: self.parse_inline_light_point, + 2: self.parse_group, + 73: self.parse_lod, + 4: self.parse_object, + 10: self.parse_push, + 11: self.parse_pop, + 96: self.parse_unhandled, + 14: self.parse_unhandled, + 91: self.parse_unhandled, + 98: self.parse_unhandled, + 63: self.parse_xref}) + self.props = dict.fromkeys(['type', 'id', 'comment', 'priority', 'flags', 'special1', + 'special2', 'significance', 'layer code', 'loop count', + 'loop duration', 'last frame duration']) + + self.props['type'] = 'Group' + self.props['comment'] = '' + self.props['id'] = self.header.fw.read_string(8) + self.props['priority'] = self.header.fw.read_short() + self.header.fw.read_ahead(2) + self.props['flags'] = self.header.fw.read_int() + self.props['special1'] = self.header.fw.read_short() + self.props['special2'] = self.header.fw.read_short() + self.props['significance'] = self.header.fw.read_short() + self.props['layer code'] = self.header.fw.read_char() + self.header.fw.read_ahead(5) + self.props['loop count'] = self.header.fw.read_int() + self.props['loop duration'] = self.header.fw.read_float() + self.props['last frame duration'] = self.header.fw.read_float() + +class XRef(InterNode): + def parse(self): + if self.xref: + self.xref.parse() + Node.parse(self) + + def __init__(self, parent): + Node.__init__(self, parent, parent.header) + InterNode.__init__(self) + + self.root_handler.set_handler({49: self.parse_matrix}) + self.root_handler.set_throw_back_lst(throw_back_opcodes) + + xref_filename = self.header.fw.read_string(200) + filename = FF.find(xref_filename) + + self.props['type'] = 'XRef' + + if filename != None: + self.xref = Database(filename, self) + self.props['id'] = 'X: ' + Blender.sys.splitext(Blender.sys.basename(filename))[0] + else: + self.xref = None + self.props['id'] = 'X: broken' + +class LOD(InterNode): + def blender_import(self): + self.move_to_next_layer() + InterNode.blender_import(self) + + def __init__(self, parent): + Node.__init__(self, parent, parent.header) + InterNode.__init__(self) + + self.root_handler.set_handler({33: self.parse_long_id, + 31: self.parse_comment, + 10: self.parse_push, + 49: self.parse_matrix}) + self.root_handler.set_throw_back_lst(throw_back_opcodes) + + self.child_handler.set_handler({2: self.parse_group, + 73: self.parse_lod, + 4: self.parse_object, + 10: self.parse_push, + 11: self.parse_pop, + 96: self.parse_unhandled, # switch + 14: self.parse_unhandled, # DOF + 91: self.parse_unhandled, # sound + 98: self.parse_unhandled, # clip + 63: self.parse_xref}) + + self.props['type'] = 'LOD' + self.props['id'] = self.header.fw.read_string(8) + +class InlineLightPoint(InterNode): + # return dictionary: lp_app name => index list + def group_points(self, props): + + name_to_indices = {} + + for i in self.indices: + vert_desc = self.header.vert_pal.vert_desc_lst[i] + app_desc = LightPointAppDesc() + app_desc.props.update(props) + # add vertex normal and color + app_desc.props.update({'nx': vert_desc.nx}) + app_desc.props.update({'ny': vert_desc.ny}) + app_desc.props.update({'nz': vert_desc.nz}) + + app_desc.props.update({'r': vert_desc.r}) + app_desc.props.update({'g': vert_desc.g}) + app_desc.props.update({'b': vert_desc.b}) + app_desc.props.update({'a': vert_desc.a}) + + app_name = GRR.request_lightpoint_app(app_desc) + + if name_to_indices.get(app_name): + name_to_indices[app_name].append(i) + else: + name_to_indices.update({app_name: [i]}) + + return name_to_indices + + def blender_import(self): + name = self.props['type'] + ': ' + self.props['id'] + + name_to_indices = self.group_points(self.app_props) + + for app_name, indices in name_to_indices.items(): + self.object = Blender.Object.New('Mesh', name) + self.mesh = self.object.getData() + + if self.parent: + self.parent.object.makeParent([self.object]) + + for i in indices: + vert = self.header.vert_pal.blender_verts[i] + self.mesh.verts.append(vert) + + scene.link(self.object) + + self.object.Layer = current_layer + + if self.matrix: + self.object.setMatrix(self.matrix) + + # Import comment. + if self.props['comment'] != '': + name = 'COMMENT: ' + self.props['id'] + t = Blender.Text.New(name) + t.write(self.props['comment']) + self.props['comment'] = name + + # Attach properties. + self.props.update({'appearance': app_name}) + for name, value in self.props.items(): + self.object.addProperty(name, value) + + self.mesh.update() + + def parse_vertex_list(self): + length = self.header.fw.get_length() + fw = self.header.fw + vert_pal = self.header.vert_pal + + count = (length-4)/4 + + for i in range(count): + byte_offset = fw.read_int() + if byte_offset in vert_pal.index: + index = vert_pal.index[byte_offset] + self.indices.append(index) + elif options.verbose >= 1: + print 'Warning: Unable to map byte offset %s' + \ + ' to vertex index.' % byte_offset + + return True + + def __init__(self, parent): + Node.__init__(self, parent, parent.header) + InterNode.__init__(self) + self.root_handler.set_handler({33: self.parse_long_id, + 31: self.parse_comment, + 10: self.parse_push, + 49: self.parse_matrix}) + self.root_handler.set_throw_back_lst(throw_back_opcodes) + + self.child_handler.set_handler({72: self.parse_vertex_list, + 10: self.parse_push, + 11: self.parse_pop}) + + self.indices = [] + + self.props = dict.fromkeys(['id', 'type', 'comment', 'draw order', 'appearance']) + self.app_props = dict() + + self.props['comment'] = '' + self.props['type'] = 'Light Point' + self.props['id'] = self.header.fw.read_string(8) + + self.app_props.update({'smc': self.header.fw.read_short()}) + self.app_props.update({'fid': self.header.fw.read_short()}) + self.app_props.update({'back color: a': self.header.fw.read_uchar()}) + self.app_props.update({'back color: b': self.header.fw.read_uchar()}) + self.app_props.update({'back color: g': self.header.fw.read_uchar()}) + self.app_props.update({'back color: r': self.header.fw.read_uchar()}) + self.app_props.update({'display mode': self.header.fw.read_int()}) + self.app_props.update({'intensity': self.header.fw.read_float()}) + self.app_props.update({'back intensity': self.header.fw.read_float()}) + self.app_props.update({'minimum defocus': self.header.fw.read_float()}) + self.app_props.update({'maximum defocus': self.header.fw.read_float()}) + self.app_props.update({'fading mode': self.header.fw.read_int()}) + self.app_props.update({'fog punch mode': self.header.fw.read_int()}) + self.app_props.update({'directional mode': self.header.fw.read_int()}) + self.app_props.update({'range mode': self.header.fw.read_int()}) + self.app_props.update({'min pixel size': self.header.fw.read_float()}) + self.app_props.update({'max pixel size': self.header.fw.read_float()}) + self.app_props.update({'actual size': self.header.fw.read_float()}) + self.app_props.update({'trans falloff pixel size': self.header.fw.read_float()}) + self.app_props.update({'trans falloff exponent': self.header.fw.read_float()}) + self.app_props.update({'trans falloff scalar': self.header.fw.read_float()}) + self.app_props.update({'trans falloff clamp': self.header.fw.read_float()}) + self.app_props.update({'fog scalar': self.header.fw.read_float()}) + self.app_props.update({'fog intensity': self.header.fw.read_float()}) + self.app_props.update({'size threshold': self.header.fw.read_float()}) + self.app_props.update({'directionality': self.header.fw.read_int()}) + self.app_props.update({'horizontal lobe angle': self.header.fw.read_float()}) + self.app_props.update({'vertical lobe angle': self.header.fw.read_float()}) + self.app_props.update({'lobe roll angle': self.header.fw.read_float()}) + self.app_props.update({'dir falloff exponent': self.header.fw.read_float()}) + self.app_props.update({'dir ambient intensity': self.header.fw.read_float()}) + self.header.fw.read_ahead(12) # Animation settings. + self.app_props.update({'significance': self.header.fw.read_float()}) + self.props['draw order'] = self.header.fw.read_int() + self.app_props.update({'flags': self.header.fw.read_int()}) + #self.fw.read_ahead(12) # More animation settings. + +class IndexedLightPoint(InterNode): + # return dictionary: lp_app name => index list + def group_points(self, props): + + name_to_indices = {} + + for i in self.indices: + vert_desc = self.header.vert_pal.vert_desc_lst[i] + app_desc = LightPointAppDesc() + app_desc.props.update(props) + # add vertex normal and color + app_desc.props.update({'nx': vert_desc.nx}) + app_desc.props.update({'ny': vert_desc.ny}) + app_desc.props.update({'nz': vert_desc.nz}) + + app_desc.props.update({'r': vert_desc.r}) + app_desc.props.update({'g': vert_desc.g}) + app_desc.props.update({'b': vert_desc.b}) + app_desc.props.update({'a': vert_desc.a}) + + app_name = GRR.request_lightpoint_app(app_desc) + + if name_to_indices.get(app_name): + name_to_indices[app_name].append(i) + else: + name_to_indices.update({app_name: [i]}) + + return name_to_indices + + def blender_import(self): + name = self.props['type'] + ': ' + self.props['id'] + + name_to_indices = self.group_points(self.header.lightpoint_appearance_pal[self.index]) + + for app_name, indices in name_to_indices.items(): + self.object = Blender.Object.New('Mesh', name) + self.mesh = self.object.getData() + + if self.parent: + self.parent.object.makeParent([self.object]) + + for i in indices: + vert = self.header.vert_pal.blender_verts[i] + self.mesh.verts.append(vert) + + scene.link(self.object) + + self.object.Layer = current_layer + + if self.matrix: + self.object.setMatrix(self.matrix) + + # Import comment. + if self.props['comment'] != '': + name = 'COMMENT: ' + self.props['id'] + t = Blender.Text.New(name) + t.write(self.props['comment']) + self.props['comment'] = name + + # Attach properties. + self.props.update({'appearance': app_name}) + for name, value in self.props.items(): + self.object.addProperty(name, value) + + self.mesh.update() + + def parse_vertex_list(self): + length = self.header.fw.get_length() + fw = self.header.fw + vert_pal = self.header.vert_pal + + count = (length-4)/4 + + for i in range(count): + byte_offset = fw.read_int() + if byte_offset in vert_pal.index: + index = vert_pal.index[byte_offset] + self.indices.append(index) + elif options.verbose >= 1: + print 'Warning: Unable to map byte offset %s' + \ + ' to vertex index.' % byte_offset + + return True + + def __init__(self, parent): + Node.__init__(self, parent, parent.header) + InterNode.__init__(self) + self.root_handler.set_handler({33: self.parse_long_id, + 31: self.parse_comment, + 10: self.parse_push, + 49: self.parse_matrix}) + self.root_handler.set_throw_back_lst(throw_back_opcodes) + + self.child_handler.set_handler({72: self.parse_vertex_list, + 10: self.parse_push, + 11: self.parse_pop}) + + self.indices = [] + + self.props = dict.fromkeys(['id', 'type', 'comment', 'draw order', 'appearance']) + self.props['comment'] = '' + self.props['type'] = 'Light Point' + self.props['id'] = self.header.fw.read_string(8) + self.index = self.header.fw.read_int() + self.header.fw.read_ahead(4) # animation index + self.props['draw order'] = self.header.fw.read_int() + +class Unhandled(InterNode): + def __init__(self, parent): + Node.__init__(self, parent, parent.header) + InterNode.__init__(self) + + self.root_handler.set_handler({33: self.parse_long_id, + 31: self.parse_comment, + 10: self.parse_push, + 49: self.parse_matrix}) + self.root_handler.set_throw_back_lst(throw_back_opcodes) + + self.child_handler.set_handler({2: self.parse_group, + 73: self.parse_lod, + 4: self.parse_object, + 10: self.parse_push, + 11: self.parse_pop, + 96: self.parse_unhandled, # switch + 14: self.parse_unhandled, # DOF + 91: self.parse_unhandled, # sound + 98: self.parse_unhandled, # clip + 63: self.parse_xref}) + + self.props['id'] = self.header.fw.read_string(8) + +class Database(InterNode): + def blender_import(self): + self.tex_pal = dict(self.tex_pal_lst) + del self.tex_pal_lst + + # Setup Textures + bl_tex_pal_lst = [] + for i in self.tex_pal.keys(): + path_filename = FF.find(self.tex_pal[i]) + if path_filename != None: + img = GRR.request_image(path_filename) + if img: + tex = GRR.request_texture(img) + tex.setName(FF.strip_path(self.tex_pal[i])) + bl_tex_pal_lst.append( (i, tex) ) + else: + bl_tex_pal_lst.append( (i, None) ) + elif options.verbose >= 1: + print 'Warning: Unable to find', self.tex_pal[i] + + self.bl_tex_pal = dict(bl_tex_pal_lst) + + # Setup Materials + self.mat_desc_pal = dict(self.mat_desc_pal_lst) + + InterNode.blender_import(self) + + def parse_appearance_palette(self): + props = dict() + self.fw.read_ahead(4) # reserved + props.update({'id': self.fw.read_string(256)}) + index = self.fw.read_int() + props.update({'smc': self.fw.read_short()}) + props.update({'fid': self.fw.read_short()}) + props.update({'back color: a': self.fw.read_uchar()}) + props.update({'back color: b': self.fw.read_uchar()}) + props.update({'back color: g': self.fw.read_uchar()}) + props.update({'back color: r': self.fw.read_uchar()}) + props.update({'display mode': self.fw.read_int()}) + props.update({'intensity': self.fw.read_float()}) + props.update({'back intensity': self.fw.read_float()}) + props.update({'minimum defocus': self.fw.read_float()}) + props.update({'maximum defocus': self.fw.read_float()}) + props.update({'fading mode': self.fw.read_int()}) + props.update({'fog punch mode': self.fw.read_int()}) + props.update({'directional mode': self.fw.read_int()}) + props.update({'range mode': self.fw.read_int()}) + props.update({'min pixel size': self.fw.read_float()}) + props.update({'max pixel size': self.fw.read_float()}) + props.update({'actual size': self.fw.read_float()}) + props.update({'trans falloff pixel size': self.fw.read_float()}) + props.update({'trans falloff exponent': self.fw.read_float()}) + props.update({'trans falloff scalar': self.fw.read_float()}) + props.update({'trans falloff clamp': self.fw.read_float()}) + props.update({'fog scalar': self.fw.read_float()}) + props.update({'fog intensity': self.fw.read_float()}) + props.update({'size threshold': self.fw.read_float()}) + props.update({'directionality': self.fw.read_int()}) + props.update({'horizontal lobe angle': self.fw.read_float()}) + props.update({'vertical lobe angle': self.fw.read_float()}) + props.update({'lobe roll angle': self.fw.read_float()}) + props.update({'dir falloff exponent': self.fw.read_float()}) + props.update({'dir ambient intensity': self.fw.read_float()}) + props.update({'significance': self.fw.read_float()}) + props.update({'flags': self.fw.read_int()}) + props.update({'visibility range': self.fw.read_float()}) + props.update({'fade range ratio': self.fw.read_float()}) + props.update({'fade in duration': self.fw.read_float()}) + props.update({'fade out duration': self.fw.read_float()}) + props.update({'LOD range ratio': self.fw.read_float()}) + props.update({'LOD scale': self.fw.read_float()}) + + self.lightpoint_appearance_pal.update({index: props}) + + def parse_header(self): + self.props['type'] = 'Header' + self.props['comment'] = '' + self.props['id'] = self.fw.read_string(8) + self.props['version'] = self.fw.read_int() + self.fw.read_ahead(46) + self.props['units'] = self.fw.read_char() + self.props['set white'] = bool(self.fw.read_char()) + self.props['flags'] = self.fw.read_int() + self.fw.read_ahead(24) + self.props['projection type'] = self.fw.read_int() + self.fw.read_ahead(36) + self.props['sw x'] = self.fw.read_double() + self.props['sw y'] = self.fw.read_double() + self.props['dx'] = self.fw.read_double() + self.props['dy'] = self.fw.read_double() + self.fw.read_ahead(24) + self.props['sw lat'] = self.fw.read_double() + self.props['sw lon'] = self.fw.read_double() + self.props['ne lat'] = self.fw.read_double() + self.props['ne lon'] = self.fw.read_double() + self.props['origin lat'] = self.fw.read_double() + self.props['origin lon'] = self.fw.read_double() + self.props['lambert lat1'] = self.fw.read_double() + self.props['lambert lat2'] = self.fw.read_double() + self.fw.read_ahead(16) + self.props['ellipsoid model'] = self.fw.read_int() + self.fw.read_ahead(4) + self.props['utm zone'] = self.fw.read_short() + self.fw.read_ahead(6) + self.props['dz'] = self.fw.read_double() + self.props['radius'] = self.fw.read_double() + self.fw.read_ahead(8) + self.props['major axis'] = self.fw.read_double() + self.props['minor axis'] = self.fw.read_double() + + if options.verbose >= 1: + print 'OpenFlight Version:', float(self.props['version']) / 100.0 + print + + return True + + def parse_mat_palette(self): + mat_desc = MaterialDesc() + index = self.fw.read_int() + + name = self.fw.read_string(12) + if len(mat_desc.name) > 0: + mat_desc.name = name + + flag = self.fw.read_int() + # skip material if not used + if not flag & 0x80000000: + return True + + ambient_col = [self.fw.read_float(), self.fw.read_float(), self.fw.read_float()] + mat_desc.diffuse = [self.fw.read_float(), self.fw.read_float(), self.fw.read_float()] + mat_desc.specular = [self.fw.read_float(), self.fw.read_float(), self.fw.read_float()] + emissive_col = [self.fw.read_float(), self.fw.read_float(), self.fw.read_float()] + + mat_desc.shininess = self.fw.read_float() / 64.0 # [0.0, 128.0] => [0.0, 2.0] + mat_desc.alpha = self.fw.read_float() + + # Convert ambient and emissive colors into intensitities. + mat_desc.ambient = col_to_gray(ambient_col) + mat_desc.emissive = col_to_gray(emissive_col) + + self.mat_desc_pal_lst.append( (index, mat_desc) ) + + return True + + def get_color(self, color_index): + index = color_index / 128 + intensity = float(color_index - 128.0 * index) / 127.0 + + if index >= 0 and index <= 1023: + brightest = self.col_pal[index] + r = int(brightest[0] * intensity) + g = int(brightest[1] * intensity) + b = int(brightest[2] * intensity) + #a = int(brightest[3] * intensity) + a = int(brightest[3]) + + color = [r, g, b, a] + + return color + + def parse_color_palette(self): + self.header.fw.read_ahead(128) + for i in range(1024): + a = self.header.fw.read_uchar() + b = self.header.fw.read_uchar() + g = self.header.fw.read_uchar() + r = self.header.fw.read_uchar() + self.col_pal.append((r, g, b, a)) + return True + + def parse_vertex_palette(self): + self.vert_pal = VertexPalette(self) + self.vert_pal.parse() + return True + + def parse_texture_palette(self): + name = self.fw.read_string(200) + index = self.fw.read_int() + self.tex_pal_lst.append( (index, name) ) + return True + + def __init__(self, filename, parent=None): + if options.verbose >= 1: + print 'Parsing:', filename + print + + self.fw = FltIn(filename) + Node.__init__(self, parent, self) + InterNode.__init__(self) + + self.root_handler.set_handler({1: self.parse_header, + 67: self.parse_vertex_palette, + 33: self.parse_long_id, + 31: self.parse_comment, + 64: self.parse_texture_palette, + 32: self.parse_color_palette, + 113: self.parse_mat_palette, + 128: self.parse_appearance_palette, + 10: self.parse_push}) + if parent: + self.root_handler.set_throw_back_lst(throw_back_opcodes) + + self.child_handler.set_handler({#130: self.parse_indexed_light_point, + #111: self.parse_inline_light_point, + 2: self.parse_group, + 73: self.parse_lod, + 4: self.parse_object, + 10: self.parse_push, + 11: self.parse_pop, + 96: self.parse_unhandled, + 14: self.parse_unhandled, + 91: self.parse_unhandled, + 98: self.parse_unhandled, + 63: self.parse_xref}) + + self.vert_pal = None + self.lightpoint_appearance_pal = dict() + self.tex_pal = dict() + self.tex_pal_lst = [] + self.bl_tex_pal = dict() + self.col_pal = [] + self.mat_desc_pal_lst = [] + self.mat_desc_pal = dict() + self.props = dict.fromkeys(['id', 'type', 'comment', 'version', 'units', 'set white', + 'flags', 'projection type', 'sw x', 'sw y', 'dx', 'dy', 'dz', 'sw lat', + 'sw lon', 'ne lat', 'ne lon', 'origin lat', 'origin lon', 'lambert lat1', + 'lambert lat2', 'ellipsoid model', 'utm zone', 'radius', 'major axis', 'minor axis']) + +def select_file(filename): + Blender.Window.WaitCursor(True) + + if filename[-4:] != '.flt': + msg = 'Error: Not a flight file.' + Blender.Draw.PupMenu(msg) + print msg + print + return + + if not Blender.sys.exists(filename): + msg = 'Error: File ' + filename + ' does not exist.' + Blender.Draw.PupMenu(msg) + return + + FF.add_file_to_search_path(filename) + + if options.verbose >= 1: + print 'Pass 1: Loading.' + print + + load_time = Blender.sys.time() + db = Database(filename) + db.parse() + load_time = Blender.sys.time() - load_time + + if options.verbose >= 1: + print + print 'Pass 2: Importing to Blender.' + print + + import_time = Blender.sys.time() + db.blender_import() + import_time = Blender.sys.time() - import_time + + Blender.Window.ViewLayer(range(1,21)) + Blender.Window.RedrawAll() + + if options.verbose >= 1: + print 'Done.' + print + print 'Time to parse file: %.3f seconds' % load_time + print 'Time to import to blender: %.3f seconds' % import_time + print 'Total time: %.3f seconds' % (load_time + import_time) + + Blender.Window.WaitCursor(False) + + +if options.verbose >= 1: + print + print 'OpenFlight Importer' + print 'Version:', __version__ + print 'Author: Greg MacDonald' + print __url__[2] + print + +Blender.Window.EditMode(0) + +winid = Blender.Window.GetScreenInfo(Blender.Window.Types.VIEW3D)[0]['id'] + +Blender.Window.FileSelector(select_file, "Import OpenFlight", "*.flt") diff --git a/release/scripts/md2_export.py b/release/scripts/md2_export.py new file mode 100644 index 00000000000..d02f1101d9a --- /dev/null +++ b/release/scripts/md2_export.py @@ -0,0 +1,1016 @@ +#!BPY + +""" +Name: 'MD2 (.md2)' +Blender: 239 +Group: 'Export' +Tooltip: 'Export to Quake file format (.md2).' +""" + +__author__ = 'Bob Holcomb' +__version__ = '0.16' +__url__ = ["Bob's site, http://bane.servebeer.com", + "Support forum, http://scourage.servebeer.com/phpbb/", "blender", "elysiun"] +__email__ = ["Bob Holcomb, bob_holcomb:hotmail*com", "scripts"] +__bpydoc__ = """\ +This script Exports a Quake 2 file (MD2). + + Additional help from: Shadwolf, Skandal, Rojo, Cambo<br> + Thanks Guys! +""" + +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# Script copyright (C): Bob Holcomb +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# ***** END GPL LICENCE BLOCK ***** +# -------------------------------------------------------------------------- + + +import Blender +from Blender import * +from Blender.Draw import * +from Blender.BGL import * +from Blender.Window import * + +import struct, string +from types import * + + + +###################################################### +# GUI Loader +###################################################### + +# Export globals +g_filename=Create("/home/bob/work/blender_scripts/md2/test-export.md2") +g_frame_filename=Create("default") + +g_filename_search=Create("model") +g_frame_search=Create("default") + +user_frame_list=[] + +#Globals +g_scale=Create(1.0) + +# Events +EVENT_NOEVENT=1 +EVENT_SAVE_MD2=2 +EVENT_CHOOSE_FILENAME=3 +EVENT_CHOOSE_FRAME=4 +EVENT_EXIT=100 + +###################################################### +# Callbacks for Window functions +###################################################### +def filename_callback(input_filename): + global g_filename + g_filename.val=input_filename + +def frame_callback(input_frame): + global g_frame_filename + g_frame_filename.val=input_frame + +def draw_gui(): + global g_scale + global g_filename + global g_frame_filename + global EVENT_NOEVENT,EVENT_SAVE_MD2,EVENT_CHOOSE_FILENAME,EVENT_CHOOSE_FRAME,EVENT_EXIT + + ########## Titles + glClear(GL_COLOR_BUFFER_BIT) + glRasterPos2d(8, 103) + Text("MD2 Export") + + ######### Parameters GUI Buttons + g_filename = String("MD2 file to save: ", EVENT_NOEVENT, 10, 55, 210, 18, + g_filename.val, 255, "MD2 file to save") + ########## MD2 File Search Button + Button("Search",EVENT_CHOOSE_FILENAME,220,55,80,18) + + g_frame_filename = String("Frame List file to load: ", EVENT_NOEVENT, 10, 35, 210, 18, + g_frame_filename.val, 255, "Frame List to load-overrides MD2 defaults") + ########## Texture Search Button + Button("Search",EVENT_CHOOSE_FRAME,220,35,80,18) + + ########## Scale slider-default is 1/8 which is a good scale for md2->blender + g_scale= Slider("Scale Factor: ", EVENT_NOEVENT, 10, 75, 210, 18, + 1.0, 0.001, 10.0, 1, "Scale factor for obj Model"); + + ######### Draw and Exit Buttons + Button("Export",EVENT_SAVE_MD2 , 10, 10, 80, 18) + Button("Exit",EVENT_EXIT , 170, 10, 80, 18) + +def event(evt, val): + if (evt == QKEY and not val): + Exit() + +def bevent(evt): + global g_filename + global g_frame_filename + global EVENT_NOEVENT,EVENT_SAVE_MD2,EVENT_EXIT + + ######### Manages GUI events + if (evt==EVENT_EXIT): + Blender.Draw.Exit() + elif (evt==EVENT_CHOOSE_FILENAME): + FileSelector(filename_callback, "MD2 File Selection") + elif (evt==EVENT_CHOOSE_FRAME): + FileSelector(frame_callback, "Frame Selection") + elif (evt==EVENT_SAVE_MD2): + if (g_filename.val == "model"): + save_md2("blender.md2") + Blender.Draw.Exit() + return + else: + save_md2(g_filename.val) + Blender.Draw.Exit() + return + +Register(draw_gui, event, bevent) + +###################################################### +# MD2 Model Constants +###################################################### +MD2_MAX_TRIANGLES=4096 +MD2_MAX_VERTICES=2048 +MD2_MAX_TEXCOORDS=2048 +MD2_MAX_FRAMES=512 +MD2_MAX_SKINS=32 +MD2_MAX_FRAMESIZE=(MD2_MAX_VERTICES * 4 + 128) + +MD2_FRAME_NAME_LIST=(("stand",1,40), + ("run",41,46), + ("attack",47,54), + ("pain1",55,58), + ("pain2",59,62), + ("pain3",63,66), + ("jump",67,72), + ("flip",73,84), + ("salute", 85,95), + ("taunt",96,112), + ("wave",113,123), + ("point",124,135), + ("crstnd",136,154), + ("crwalk",155,160), + ("crattack",161,169), + ("crpain",170,173), + ("crdeath",174,178), + ("death1",179,184), + ("death2",185,190), + ("death3",191,198)) + #198 frames + +###################################################### +# MD2 data structures +###################################################### +class md2_point: + vertices=[] + lightnormalindex=0 + binary_format="<3BB" + def __init__(self): + self.vertices=[0]*3 + self.lightnormalindex=0 + def save(self, file): + temp_data=[0]*4 + temp_data[0]=self.vertices[0] + temp_data[1]=self.vertices[1] + temp_data[2]=self.vertices[2] + temp_data[3]=self.lightnormalindex + data=struct.pack(self.binary_format, temp_data[0], temp_data[1], temp_data[2], temp_data[3]) + file.write(data) + def dump(self): + print "MD2 Point Structure" + print "vertex X: ", self.vertices[0] + print "vertex Y: ", self.vertices[1] + print "vertex Z: ", self.vertices[2] + print "lightnormalindex: ",self.lightnormalindex + print "" + +class md2_face: + vertex_index=[] + texture_index=[] + binary_format="<3h3h" + def __init__(self): + self.vertex_index = [ 0, 0, 0 ] + self.texture_index = [ 0, 0, 0] + def save(self, file): + temp_data=[0]*6 + #swap vertices around so they draw right + temp_data[0]=self.vertex_index[0] + temp_data[1]=self.vertex_index[2] + temp_data[2]=self.vertex_index[1] + #swap texture vertices around so they draw right + temp_data[3]=self.texture_index[0] + temp_data[4]=self.texture_index[2] + temp_data[5]=self.texture_index[1] + data=struct.pack(self.binary_format,temp_data[0],temp_data[1],temp_data[2],temp_data[3],temp_data[4],temp_data[5]) + file.write(data) + def dump (self): + print "MD2 Face Structure" + print "vertex 1 index: ", self.vertex_index[0] + print "vertex 2 index: ", self.vertex_index[1] + print "vertex 3 index: ", self.vertex_index[2] + print "texture 1 index: ", self.texture_index[0] + print "texture 2 index: ", self.texture_index[1] + print "texture 3 index: ", self.texture_index[2] + print "" + +class md2_tex_coord: + u=0 + v=0 + binary_format="<2h" + def __init__(self): + self.u=0 + self.v=0 + def save(self, file): + temp_data=[0]*2 + temp_data[0]=self.u + temp_data[1]=self.v + data=struct.pack(self.binary_format, temp_data[0], temp_data[1]) + file.write(data) + def dump (self): + print "MD2 Texture Coordinate Structure" + print "texture coordinate u: ",self.u + print "texture coordinate v: ",self.v + print "" + +class md2_GL_command: + s=0.0 + t=0.0 + vert_index=0 + binary_format="<2fi" + + def __init__(self): + self.s=0.0 + self.t=0.0 + vert_index=0 + def save(self,file): + temp_data=[0]*3 + temp_data[0]=float(self.s) + temp_data[1]=float(self.t) + temp_data[2]=self.vert_index + data=struct.pack(self.binary_format, temp_data[0],temp_data[1],temp_data[2]) + file.write(data) + def dump (self): + print "MD2 OpenGL Command" + print "s: ", self.s + print "t: ", self.t + print "Vertex Index: ", self.vert_index + print "" + +class md2_GL_cmd_list: + num=0 + cmd_list=[] + binary_format="<i" + + def __init__(self): + self.num=0 + self.cmd_list=[] + + def save(self,file): + data=struct.pack(self.binary_format, self.num) + file.write(data) + for cmd in self.cmd_list: + cmd.save(file) + def dump(self): + print "MD2 OpenGL Command List" + print "number: ", self.num + for cmd in self.cmd_list: + cmd.dump() + print "" + +class md2_skin: + name="" + binary_format="<64s" + def __init__(self): + self.name="" + def save(self, file): + temp_data=self.name + data=struct.pack(self.binary_format, temp_data) + file.write(data) + def dump (self): + print "MD2 Skin" + print "skin name: ",self.name + print "" + +class md2_frame: + scale=[] + translate=[] + name=[] + vertices=[] + binary_format="<3f3f16s" + + def __init__(self): + self.scale=[0.0]*3 + self.translate=[0.0]*3 + self.name="" + self.vertices=[] + def save(self, file): + temp_data=[0]*7 + temp_data[0]=float(self.scale[0]) + temp_data[1]=float(self.scale[1]) + temp_data[2]=float(self.scale[2]) + temp_data[3]=float(self.translate[0]) + temp_data[4]=float(self.translate[1]) + temp_data[5]=float(self.translate[2]) + temp_data[6]=self.name + data=struct.pack(self.binary_format, temp_data[0],temp_data[1],temp_data[2],temp_data[3],temp_data[4],temp_data[5],temp_data[6]) + file.write(data) + def dump (self): + print "MD2 Frame" + print "scale x: ",self.scale[0] + print "scale y: ",self.scale[1] + print "scale z: ",self.scale[2] + print "translate x: ",self.translate[0] + print "translate y: ",self.translate[1] + print "translate z: ",self.translate[2] + print "name: ",self.name + print "" + +class md2_obj: + #Header Structure + ident=0 #int 0 This is used to identify the file + version=0 #int 1 The version number of the file (Must be 8) + skin_width=0 #int 2 The skin width in pixels + skin_height=0 #int 3 The skin height in pixels + frame_size=0 #int 4 The size in bytes the frames are + num_skins=0 #int 5 The number of skins associated with the model + num_vertices=0 #int 6 The number of vertices (constant for each frame) + num_tex_coords=0 #int 7 The number of texture coordinates + num_faces=0 #int 8 The number of faces (polygons) + num_GL_commands=0 #int 9 The number of gl commands + num_frames=0 #int 10 The number of animation frames + offset_skins=0 #int 11 The offset in the file for the skin data + offset_tex_coords=0 #int 12 The offset in the file for the texture data + offset_faces=0 #int 13 The offset in the file for the face data + offset_frames=0 #int 14 The offset in the file for the frames data + offset_GL_commands=0#int 15 The offset in the file for the gl commands data + offset_end=0 #int 16 The end of the file offset + binary_format="<17i" #little-endian (<), 17 integers (17i) + #md2 data objects + tex_coords=[] + faces=[] + frames=[] + skins=[] + GL_commands=[] + + def __init__ (self): + self.tex_coords=[] + self.faces=[] + self.frames=[] + self.skins=[] + def save(self, file): + temp_data=[0]*17 + temp_data[0]=self.ident + temp_data[1]=self.version + temp_data[2]=self.skin_width + temp_data[3]=self.skin_height + temp_data[4]=self.frame_size + temp_data[5]=self.num_skins + temp_data[6]=self.num_vertices + temp_data[7]=self.num_tex_coords + temp_data[8]=self.num_faces + temp_data[9]=self.num_GL_commands + temp_data[10]=self.num_frames + temp_data[11]=self.offset_skins + temp_data[12]=self.offset_tex_coords + temp_data[13]=self.offset_faces + temp_data[14]=self.offset_frames + temp_data[15]=self.offset_GL_commands + temp_data[16]=self.offset_end + data=struct.pack(self.binary_format, temp_data[0],temp_data[1],temp_data[2],temp_data[3],temp_data[4],temp_data[5],temp_data[6],temp_data[7],temp_data[8],temp_data[9],temp_data[10],temp_data[11],temp_data[12],temp_data[13],temp_data[14],temp_data[15],temp_data[16]) + file.write(data) + #write the skin data + for skin in self.skins: + skin.save(file) + #save the texture coordinates + for tex_coord in self.tex_coords: + tex_coord.save(file) + #save the face info + for face in self.faces: + face.save(file) + #save the frames + for frame in self.frames: + frame.save(file) + for vert in frame.vertices: + vert.save(file) + #save the GL command List + for cmd in self.GL_commands: + cmd.save(file) + def dump (self): + print "Header Information" + print "ident: ", self.ident + print "version: ", self.version + print "skin width: ", self.skin_width + print "skin height: ", self.skin_height + print "frame size: ", self.frame_size + print "number of skins: ", self.num_skins + print "number of texture coordinates: ", self.num_tex_coords + print "number of faces: ", self.num_faces + print "number of frames: ", self.num_frames + print "number of vertices: ", self.num_vertices + print "number of GL commands: ",self.num_GL_commands + print "offset skins: ", self.offset_skins + print "offset texture coordinates: ", self.offset_tex_coords + print "offset faces: ", self.offset_faces + print "offset frames: ",self.offset_frames + print "offset GL Commands: ",self.offset_GL_commands + print "offset end: ",self.offset_end + print "" + +###################################################### +# Validation +###################################################### +def validation(object): + global user_frame_list + + #get access to the mesh data + mesh=object.getData(False, True) #get the object (not just name) and the Mesh, not NMesh + + #check it's composed of only tri's + result=0 + for face in mesh.faces: + if len(face.verts)!=3: + #select the face for future triangulation + face.sel=1 + if result==0: #first time we have this problem, don't pop-up a window every time it finds a quad + print "Model not made entirely of triangles" + result=Blender.Draw.PupMenu("Model not made entirely out of Triangles-Convert?%t|YES|NO") + if result==1: + mesh.quadToTriangle(0) #use closest verticies in breaking a quad + elif result==2: + return False #user will fix (I guess) + + #check it has UV coordinates + if mesh.vertexUV==True: + print "Vertex UV not supported" + result=Blender.Draw.PupMenu("Vertex UV not suppored-Use Sticky UV%t|OK") + return False + + elif mesh.faceUV==True: + for face in mesh.faces: + if(len(face.uv)==3): + pass + else: + print "Models vertices do not all have UV" + result=Blender.Draw.PupMenu("Models vertices do not all have UV%t|OK") + return False + + else: + print "Model does not have UV (face or vertex)" + result=Blender.Draw.PupMenu("Model does not have UV (face or vertex)%t|OK") + return False + + #check it has only 1 associated texture map + last_face="" + last_face=mesh.faces[0].image + if last_face=="": + print "Model does not have a texture Map" + result=Blender.Draw.PupMenu("Model does not have a texture Map%t|OK") + return False + + for face in mesh.faces: + mesh_image=face.image + if not mesh_image: + print "Model has a face without a texture Map" + result=Blender.Draw.PupMenu("Model has a face without a texture Map%t|OK") + return False + if mesh_image!=last_face: + print "Model has more than 1 texture map assigned" + result=Blender.Draw.PupMenu("Model has more than 1 texture map assigned%t|OK") + return False + + size=mesh_image.getSize() + #is this really what the user wants + if (size[0]!=256 or size[1]!=256): + print "Texture map size is non-standard (not 256x256), it is: ",size[0],"x",size[1] + result=Blender.Draw.PupMenu("Texture map size is non-standard (not 256x256), it is: "+size[0]+"x"+size[1]+": Continue?%t|YES|NO") + if(result==2): + return False + + #verify frame list data + user_frame_list=get_frame_list() + temp=user_frame_list[len(user_frame_list)-1] + temp_num_frames=temp[2] + + #verify tri/vert/frame counts are within MD2 standard + face_count=len(mesh.faces) + vert_count=len(mesh.verts) + frame_count=temp_num_frames + + if face_count>MD2_MAX_TRIANGLES: + print "Number of triangles exceeds MD2 standard: ", face_count,">",MD2_MAX_TRIANGLES + result=Blender.Draw.PupMenu("Number of triangles exceeds MD2 standard: Continue?%t|YES|NO") + if(result==2): + return False + if vert_count>MD2_MAX_VERTICES: + print "Number of verticies exceeds MD2 standard",vert_count,">",MD2_MAX_VERTICES + result=Blender.Draw.PupMenu("Number of verticies exceeds MD2 standard: Continue?%t|YES|NO") + if(result==2): + return False + if frame_count>MD2_MAX_FRAMES: + print "Number of frames exceeds MD2 standard of",frame_count,">",MD2_MAX_FRAMES + result=Blender.Draw.PupMenu("Number of frames exceeds MD2 standard: Continue?%t|YES|NO") + if(result==2): + return False + #model is OK + return True + +###################################################### +# Fill MD2 data structure +###################################################### +def fill_md2(md2, object): + global user_frame_list + #get a Mesh, not NMesh + mesh=object.getData(False, True) + + #load up some intermediate data structures + tex_list={} + tex_count=0 + #create the vertex list from the first frame + Blender.Set("curframe", 1) + + #header information + md2.ident=844121161 + md2.version=8 + md2.num_vertices=len(mesh.verts) + md2.num_faces=len(mesh.faces) + + #get the skin information + #use the first faces' image for the texture information + mesh_image=mesh.faces[0].image + size=mesh_image.getSize() + md2.skin_width=size[0] + md2.skin_height=size[1] + md2.num_skins=1 + #add a skin node to the md2 data structure + md2.skins.append(md2_skin()) + md2.skins[0].name=Blender.sys.basename(mesh_image.getFilename()) + + #put texture information in the md2 structure + #build UV coord dictionary (prevents double entries-saves space) + for face in mesh.faces: + for i in range(0,3): + t=(face.uv[i]) + tex_key=(t[0],t[1]) + if not tex_list.has_key(tex_key): + tex_list[tex_key]=tex_count + tex_count+=1 + md2.num_tex_coords=tex_count #each vert has its own UV coord + + for this_tex in range (0, md2.num_tex_coords): + md2.tex_coords.append(md2_tex_coord()) + for coord, index in tex_list.iteritems(): + #md2.tex_coords.append(md2_tex_coord()) + md2.tex_coords[index].u=int(coord[0]*md2.skin_width) + md2.tex_coords[index].v=int((1-coord[1])*md2.skin_height) + + #put faces in the md2 structure + #for each face in the model + for this_face in range(0, md2.num_faces): + md2.faces.append(md2_face()) + for i in range(0,3): + #blender uses indexed vertexes so this works very well + md2.faces[this_face].vertex_index[i]=mesh.faces[this_face].verts[i].index + #lookup texture index in dictionary + uv_coord=(mesh.faces[this_face].uv[i]) + tex_key=(uv_coord[0],uv_coord[1]) + tex_index=tex_list[tex_key] + md2.faces[this_face].texture_index[i]=tex_index + + #compute GL commands + md2.num_GL_commands=build_GL_commands(md2) + + #get the frame data + #calculate 1 frame size + (1 vert size*num_verts) + md2.frame_size=40+(md2.num_vertices*4) #in bytes + + #get the frame list + user_frame_list=get_frame_list() + if user_frame_list=="default": + md2.num_frames=198 + else: + temp=user_frame_list[len(user_frame_list)-1] #last item + md2.num_frames=temp[2] #last frame number + + #fill in each frame with frame info and all the vertex data for that frame + for frame_counter in range(0,md2.num_frames): + #add a frame + md2.frames.append(md2_frame()) + #update the mesh objects vertex positions for the animation + Blender.Set("curframe", frame_counter) #set blender to the correct frame + mesh.getFromObject(object.name) #update the mesh to make verts current + +#each frame has a scale and transform value that gets the vertex value between 0-255 +#since the scale and transform are the same for the all the verts in the frame, we only need +#to figure this out once per frame + + #we need to start with the bounding box + bounding_box=object.getBoundBox() #uses the object, not the mesh data + #initialize with the first vertex for both min and max. X and Y are swapped for MD2 format + point=bounding_box[0] + frame_min_x=point[1] + frame_max_x=point[1] + frame_min_y=point[0] + frame_max_y=point[0] + frame_min_z=point[2] + frame_max_z=point[2] + #find min/max values + for point in bounding_box: + if frame_min_x>point[1]: frame_min_x=point[1] + if frame_max_x<point[1]: frame_max_x=point[1] + if frame_min_y>point[0]: frame_min_y=point[0] + if frame_max_y<point[0]: frame_max_y=point[0] + if frame_min_z>point[2]: frame_min_z=point[2] + if frame_max_z<point[2]: frame_max_z=point[2] + + #the scale is the difference between the min and max (on that axis) / 255 + frame_scale_x=(frame_max_x-frame_min_x)/255 + frame_scale_y=(frame_max_y-frame_min_y)/255 + frame_scale_z=(frame_max_z-frame_min_z)/255 + + #translate value of the mesh to center it on the origin + frame_trans_x=frame_min_x + frame_trans_y=frame_min_y + frame_trans_z=frame_min_z + + #fill in the data + md2.frames[frame_counter].scale=(-frame_scale_x, frame_scale_y, frame_scale_z) + md2.frames[frame_counter].translate=(-frame_trans_x, frame_trans_y, frame_trans_z) + + #now for the vertices + for vert_counter in range(0, md2.num_vertices): + #add a vertex to the md2 structure + md2.frames[frame_counter].vertices.append(md2_point()) + #figure out the new coords based on scale and transform + #then translates the point so it's not less than 0 + #then scale it so it's between 0..255 + new_x=int((mesh.verts[vert_counter].co[1]-frame_trans_x)/frame_scale_x) + new_y=int((mesh.verts[vert_counter].co[0]-frame_trans_y)/frame_scale_y) + new_z=int((mesh.verts[vert_counter].co[2]-frame_trans_z)/frame_scale_z) + #put them in the structure + md2.frames[frame_counter].vertices[vert_counter].vertices=(new_x, new_y, new_z) + + #need to add the lookup table check here + md2.frames[frame_counter].vertices[vert_counter].lightnormalindex=0 + + #output all the frame names-user_frame_list is loaded during the validation + for frame_set in user_frame_list: + for counter in range(frame_set[1]-1, frame_set[2]): + md2.frames[counter].name=frame_set[0]+"_"+str(counter-frame_set[1]+2) + + #compute these after everthing is loaded into a md2 structure + header_size=17*4 #17 integers, and each integer is 4 bytes + skin_size=64*md2.num_skins #64 char per skin * number of skins + tex_coord_size=4*md2.num_tex_coords #2 short * number of texture coords + face_size=12*md2.num_faces #3 shorts for vertex index, 3 shorts for tex index + frames_size=(((12+12+16)+(4*md2.num_vertices)) * md2.num_frames) #frame info+verts per frame*num frames + GL_command_size=md2.num_GL_commands*4 #each is an int or float, so 4 bytes per + + #fill in the info about offsets + md2.offset_skins=0+header_size + md2.offset_tex_coords=md2.offset_skins+skin_size + md2.offset_faces=md2.offset_tex_coords+tex_coord_size + md2.offset_frames=md2.offset_faces+face_size + md2.offset_GL_commands=md2.offset_frames+frames_size + md2.offset_end=md2.offset_GL_commands+GL_command_size + +###################################################### +# Get Frame List +###################################################### +def get_frame_list(): + global g_frame_filename + frame_list=[] + + if g_frame_filename.val=="default": + return MD2_FRAME_NAME_LIST + + else: + #check for file + if (Blender.sys.exists(g_frame_filename.val)==1): + #open file and read it in + file=open(g_frame_filename.val,"r") + lines=file.readlines() + file.close() + + #check header (first line) + if lines[0]<>"# MD2 Frame Name List\n": + print "its not a valid file" + result=Blender.Draw.PupMenu("This is not a valid frame definition file-using default%t|OK") + return MD2_FRAME_NAME_LIST + else: + #read in the data + num_frames=0 + for counter in range(1, len(lines)): + current_line=lines[counter] + if current_line[0]=="#": + #found a comment + pass + else: + data=current_line.split() + frame_list.append([data[0],num_frames+1, num_frames+int(data[1])]) + num_frames+=int(data[1]) + return frame_list + else: + print "Cannot find file" + result=Blender.Draw.PupMenu("Cannot find frame definion file-using default%t|OK") + return MD2_FRAME_NAME_LIST + +###################################################### +# Tri-Strip/Tri-Fan functions +###################################################### +def find_strip_length(md2, start_tri, start_vert): + #variables shared between fan and strip functions + global used + global strip_vert + global strip_st + global strip_tris + global strip_count + + m1=m2=0 + st1=st2=0 + + used[start_tri]=2 + + last=start_tri + + strip_vert[0]=md2.faces[last].vertex_index[start_vert%3] + strip_vert[1]=md2.faces[last].vertex_index[(start_vert+1)%3] + strip_vert[2]=md2.faces[last].vertex_index[(start_vert+2)%3] + + strip_st[0]=md2.faces[last].texture_index[start_vert%3] + strip_st[1]=md2.faces[last].texture_index[(start_vert+1)%3] + strip_st[2]=md2.faces[last].texture_index[(start_vert+2)%3] + + strip_tris[0]=start_tri + strip_count=1 + + m1=md2.faces[last].vertex_index[(start_vert+2)%3] + st1=md2.faces[last].texture_index[(start_vert+2)%3] + m2=md2.faces[last].vertex_index[(start_vert+1)%3] + st2=md2.faces[last].texture_index[(start_vert+1)%3] + + #look for matching triangle + check=start_tri+1 + + for tri_counter in range(start_tri+1, md2.num_faces): + + for k in range(0,3): + if md2.faces[check].vertex_index[k]!=m1: + continue + if md2.faces[check].texture_index[k]!=st1: + continue + if md2.faces[check].vertex_index[(k+1)%3]!=m2: + continue + if md2.faces[check].texture_index[(k+1)%3]!=st2: + continue + + #if we can't use this triangle, this tri_strip is done + if (used[tri_counter]!=0): + for clear_counter in range(start_tri+1, md2.num_faces): + if used[clear_counter]==2: + used[clear_counter]=0 + return strip_count + + #new edge + if (strip_count & 1): + m2=md2.faces[check].vertex_index[(k+2)%3] + st2=md2.faces[check].texture_index[(k+2)%3] + else: + m1=md2.faces[check].vertex_index[(k+2)%3] + st1=md2.faces[check].texture_index[(k+2)%3] + + strip_vert[strip_count+2]=md2.faces[tri_counter].vertex_index[(k+2)%3] + strip_st[strip_count+2]=md2.faces[tri_counter].texture_index[(k+2)%3] + strip_tris[strip_count]=tri_counter + strip_count+=1 + + used[tri_counter]=2 + check+=1 + return strip_count + +def find_fan_length(md2, start_tri, start_vert): + #variables shared between fan and strip functions + global used + global strip_vert + global strip_st + global strip_tris + global strip_count + + m1=m2=0 + st1=st2=0 + + used[start_tri]=2 + + last=start_tri + + strip_vert[0]=md2.faces[last].vertex_index[start_vert%3] + strip_vert[1]=md2.faces[last].vertex_index[(start_vert+1)%3] + strip_vert[2]=md2.faces[last].vertex_index[(start_vert+2)%3] + + strip_st[0]=md2.faces[last].texture_index[start_vert%3] + strip_st[1]=md2.faces[last].texture_index[(start_vert+1)%3] + strip_st[2]=md2.faces[last].texture_index[(start_vert+2)%3] + + strip_tris[0]=start_tri + strip_count=1 + + m1=md2.faces[last].vertex_index[(start_vert+0)%3] + st1=md2.faces[last].texture_index[(start_vert+0)%3] + m2=md2.faces[last].vertex_index[(start_vert+2)%3] + st2=md2.faces[last].texture_index[(start_vert+2)%3] + + #look for matching triangle + check=start_tri+1 + for tri_counter in range(start_tri+1, md2.num_faces): + for k in range(0,3): + if md2.faces[check].vertex_index[k]!=m1: + continue + if md2.faces[check].texture_index[k]!=st1: + continue + if md2.faces[check].vertex_index[(k+1)%3]!=m2: + continue + if md2.faces[check].texture_index[(k+1)%3]!=st2: + continue + + #if we can't use this triangle, this tri_strip is done + if (used[tri_counter]!=0): + for clear_counter in range(start_tri+1, md2.num_faces): + if used[clear_counter]==2: + used[clear_counter]=0 + return strip_count + + #new edge + m2=md2.faces[check].vertex_index[(k+2)%3] + st2=md2.faces[check].texture_index[(k+2)%3] + + strip_vert[strip_count+2]=m2 + strip_st[strip_count+2]=st2 + strip_tris[strip_count]=tri_counter + strip_count+=1 + + used[tri_counter]=2 + check+=1 + return strip_count + + +###################################################### +# Globals for GL command list calculations +###################################################### +used=[] +strip_vert=0 +strip_st=0 +strip_tris=0 +strip_count=0 + +###################################################### +# Build GL command List +###################################################### +def build_GL_commands(md2): + #variables shared between fan and strip functions + global used + used=[0]*md2.num_faces + global strip_vert + strip_vert=[0]*128 + global strip_st + strip_st=[0]*128 + global strip_tris + strip_tris=[0]*128 + global strip_count + strip_count=0 + + #variables + num_commands=0 + start_vert=0 + fan_length=strip_length=0 + length=best_length=0 + best_type=0 + best_vert=[0]*1024 + best_st=[0]*1024 + best_tris=[0]*1024 + s=0.0 + t=0.0 + + for face_counter in range(0,md2.num_faces): + if used[face_counter]!=0: #don't evaluate a tri that's been used + #print "found a used triangle: ", face_counter + pass + else: + best_length=0 #restart the counter + #for each vertex index in this face + for start_vert in range(0,3): + fan_length=find_fan_length(md2, face_counter, start_vert) + if (fan_length>best_length): + best_type=1 + best_length=fan_length + for index in range (0, best_length+2): + best_st[index]=strip_st[index] + best_vert[index]=strip_vert[index] + for index in range(0, best_length): + best_tris[index]=strip_tris[index] + + strip_length=find_strip_length(md2, face_counter, start_vert) + if (strip_length>best_length): + best_type=0 + best_length=strip_length + for index in range (0, best_length+2): + best_st[index]=strip_st[index] + best_vert[index]=strip_vert[index] + for index in range(0, best_length): + best_tris[index]=strip_tris[index] + + #mark the tris on the best strip/fan as used + for used_counter in range (0, best_length): + used[best_tris[used_counter]]=1 + + temp_cmdlist=md2_GL_cmd_list() + #push the number of commands into the command stream + if best_type==1: + temp_cmdlist.num=best_length+2 + num_commands+=1 + else: + temp_cmdlist.num=(-(best_length+2)) + num_commands+=1 + for command_counter in range (0, best_length+2): + #emit a vertex into the reorder buffer + cmd=md2_GL_command() + index=best_st[command_counter] + #calc and put S/T coords in the structure + s=md2.tex_coords[index].u + t=md2.tex_coords[index].v + s=(s+0.5)/md2.skin_width + t=(t+0.5)/md2.skin_height + cmd.s=s + cmd.t=t + cmd.vert_index=best_vert[command_counter] + temp_cmdlist.cmd_list.append(cmd) + num_commands+=3 + md2.GL_commands.append(temp_cmdlist) + + #end of list + temp_cmdlist=md2_GL_cmd_list() + temp_cmdlist.num=0 + md2.GL_commands.append(temp_cmdlist) + num_commands+=1 + + #cleanup and return + used=best_vert=best_st=best_tris=strip_vert=strip_st=strip_tris=0 + return num_commands + +###################################################### +# Save MD2 Format +###################################################### +def save_md2(filename): + md2=md2_obj() #blank md2 object to save + + #get the object + mesh_objs = Blender.Object.GetSelected() + + #check there is a blender object selected + if len(mesh_objs)==0: + print "Fatal Error: Must select a mesh to output as MD2" + print "Found nothing" + result=Blender.Draw.PupMenu("Must select an object to export%t|OK") + return + + mesh_obj=mesh_objs[0] #this gets the first object (should be only one) + + #check if it's a mesh object + if mesh_obj.getType()!="Mesh": + print "Fatal Error: Must select a mesh to output as MD2" + print "Found: ", mesh_obj.getType() + result=Blender.Draw.PupMenu("Selected Object must be a mesh to output as MD2%t|OK") + return + + ok=validation(mesh_obj) + if ok==False: + return + + fill_md2(md2, mesh_obj) + md2.dump() + + #actually write it to disk + file=open(filename,"wb") + md2.save(file) + file.close() + + #cleanup + md2=0 + + print "Closed the file" diff --git a/release/scripts/md2_import.py b/release/scripts/md2_import.py new file mode 100644 index 00000000000..82b76d40c94 --- /dev/null +++ b/release/scripts/md2_import.py @@ -0,0 +1,571 @@ +#!BPY + +""" +Name: 'MD2 (.md2)' +Blender: 239 +Group: 'Import' +Tooltip: 'Import from Quake file format (.md2).' +""" + +__author__ = 'Bob Holcomb' +__version__ = '0.15' +__url__ = ["Bob's site, http://bane.servebeer.com", + "Support forum, http://scourage.servebeer.com/phpbb/", "blender", "elysiun"] +__email__ = ["Bob Holcomb, bob_holcomb:hotmail*com", "scripts"] +__bpydoc__ = """\ +This script imports a Quake 2 file (MD2), textures, +and animations into blender for editing. Loader is based on MD2 loader from www.gametutorials.com-Thanks DigiBen! and the md3 blender loader by PhaethonH <phaethon@linux.ucla.edu><br> + + Additional help from: Shadwolf, Skandal, Rojo, Cambo<br> + Thanks Guys! +""" + +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# Script copyright (C) Bob Holcomb +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# ***** END GPL LICENCE BLOCK ***** +# -------------------------------------------------------------------------- + + +import Blender +from Blender import NMesh, Object, sys +from Blender.BGL import * +from Blender.Draw import * +from Blender.Window import * +from Blender.Image import * + +import struct, string +from types import * + + + + +###################################################### +# Main Body +###################################################### + +#returns the string from a null terminated string +def asciiz (s): + n = 0 + while (ord(s[n]) != 0): + n = n + 1 + return s[0:n] + + +###################################################### +# MD2 Model Constants +###################################################### +MD2_MAX_TRIANGLES=4096 +MD2_MAX_VERTICES=2048 +MD2_MAX_TEXCOORDS=2048 +MD2_MAX_FRAMES=512 +MD2_MAX_SKINS=32 +MD2_MAX_FRAMESIZE=(MD2_MAX_VERTICES * 4 + 128) + +###################################################### +# MD2 data structures +###################################################### +class md2_alias_triangle: + vertices=[] + lightnormalindex=0 + + binary_format="<3BB" #little-endian (<), 3 Unsigned char + + def __init__(self): + self.vertices=[0]*3 + self.lightnormalindex=0 + + def load(self, file): + temp_data = file.read(struct.calcsize(self.binary_format)) + data = struct.unpack(self.binary_format, temp_data) + self.vertices[0]=data[0] + self.vertices[1]=data[1] + self.vertices[2]=data[2] + self.lightnormalindex=data[3] + return self + + def dump(self): + print "MD2 Alias_Triangle Structure" + print "vertex: ", self.vertices[0] + print "vertex: ", self.vertices[1] + print "vertex: ", self.vertices[2] + print "lightnormalindex: ",self.lightnormalindex + print "" + +class md2_face: + vertex_index=[] + texture_index=[] + + binary_format="<3h3h" #little-endian (<), 3 short, 3 short + + def __init__(self): + self.vertex_index = [ 0, 0, 0 ] + self.texture_index = [ 0, 0, 0] + + def load (self, file): + temp_data=file.read(struct.calcsize(self.binary_format)) + data=struct.unpack(self.binary_format, temp_data) + self.vertex_index[0]=data[0] + self.vertex_index[1]=data[1] + self.vertex_index[2]=data[2] + self.texture_index[0]=data[3] + self.texture_index[1]=data[4] + self.texture_index[2]=data[5] + return self + + def dump (self): + print "MD2 Face Structure" + print "vertex index: ", self.vertex_index[0] + print "vertex index: ", self.vertex_index[1] + print "vertex index: ", self.vertex_index[2] + print "texture index: ", self.texture_index[0] + print "texture index: ", self.texture_index[1] + print "texture index: ", self.texture_index[2] + print "" + +class md2_tex_coord: + u=0 + v=0 + + binary_format="<2h" #little-endian (<), 2 unsigned short + + def __init__(self): + self.u=0 + self.v=0 + + def load (self, file): + temp_data=file.read(struct.calcsize(self.binary_format)) + data=struct.unpack(self.binary_format, temp_data) + self.u=data[0] + self.v=data[1] + return self + + def dump (self): + print "MD2 Texture Coordinate Structure" + print "texture coordinate u: ",self.u + print "texture coordinate v: ",self.v + print "" + + +class md2_skin: + name="" + + binary_format="<64s" #little-endian (<), char[64] + + def __init__(self): + self.name="" + + def load (self, file): + temp_data=file.read(struct.calcsize(self.binary_format)) + data=struct.unpack(self.binary_format, temp_data) + self.name=asciiz(data[0]) + return self + + def dump (self): + print "MD2 Skin" + print "skin name: ",self.name + print "" + +class md2_alias_frame: + scale=[] + translate=[] + name=[] + vertices=[] + + binary_format="<3f3f16s" #little-endian (<), 3 float, 3 float char[16] + #did not add the "3bb" to the end of the binary format + #because the alias_vertices will be read in through + #thier own loader + + def __init__(self): + self.scale=[0.0]*3 + self.translate=[0.0]*3 + self.name="" + self.vertices=[] + + + def load (self, file): + temp_data=file.read(struct.calcsize(self.binary_format)) + data=struct.unpack(self.binary_format, temp_data) + self.scale[0]=data[0] + self.scale[1]=data[1] + self.scale[2]=data[2] + self.translate[0]=data[3] + self.translate[1]=data[4] + self.translate[2]=data[5] + self.name=asciiz(data[6]) + return self + + def dump (self): + print "MD2 Alias Frame" + print "scale x: ",self.scale[0] + print "scale y: ",self.scale[1] + print "scale z: ",self.scale[2] + print "translate x: ",self.translate[0] + print "translate y: ",self.translate[1] + print "translate z: ",self.translate[2] + print "name: ",self.name + print "" + +class md2_obj: + #Header Structure + ident=0 #int 0 This is used to identify the file + version=0 #int 1 The version number of the file (Must be 8) + skin_width=0 #int 2 The skin width in pixels + skin_height=0 #int 3 The skin height in pixels + frame_size=0 #int 4 The size in bytes the frames are + num_skins=0 #int 5 The number of skins associated with the model + num_vertices=0 #int 6 The number of vertices (constant for each frame) + num_tex_coords=0 #int 7 The number of texture coordinates + num_faces=0 #int 8 The number of faces (polygons) + num_GL_commands=0 #int 9 The number of gl commands + num_frames=0 #int 10 The number of animation frames + offset_skins=0 #int 11 The offset in the file for the skin data + offset_tex_coords=0 #int 12 The offset in the file for the texture data + offset_faces=0 #int 13 The offset in the file for the face data + offset_frames=0 #int 14 The offset in the file for the frames data + offset_GL_commands=0#int 15 The offset in the file for the gl commands data + offset_end=0 #int 16 The end of the file offset + + binary_format="<17i" #little-endian (<), 17 integers (17i) + + #md2 data objects + tex_coords=[] + faces=[] + frames=[] + skins=[] + + def __init__ (self): + self.tex_coords=[] + self.faces=[] + self.frames=[] + self.skins=[] + + + def load (self, file): + temp_data = file.read(struct.calcsize(self.binary_format)) + data = struct.unpack(self.binary_format, temp_data) + + self.ident=data[0] + self.version=data[1] + + if (self.ident!=844121161 or self.version!=8): + print "Not a valid MD2 file" + Exit() + + self.skin_width=data[2] + self.skin_height=data[3] + self.frame_size=data[4] + + #make the # of skin objects for model + self.num_skins=data[5] + for i in xrange(0,self.num_skins): + self.skins.append(md2_skin()) + + self.num_vertices=data[6] + + #make the # of texture coordinates for model + self.num_tex_coords=data[7] + for i in xrange(0,self.num_tex_coords): + self.tex_coords.append(md2_tex_coord()) + + #make the # of triangle faces for model + self.num_faces=data[8] + for i in xrange(0,self.num_faces): + self.faces.append(md2_face()) + + self.num_GL_commands=data[9] + + #make the # of frames for the model + self.num_frames=data[10] + for i in xrange(0,self.num_frames): + self.frames.append(md2_alias_frame()) + #make the # of vertices for each frame + for j in xrange(0,self.num_vertices): + self.frames[i].vertices.append(md2_alias_triangle()) + + self.offset_skins=data[11] + self.offset_tex_coords=data[12] + self.offset_faces=data[13] + self.offset_frames=data[14] + self.offset_GL_commands=data[15] + + #load the skin info + file.seek(self.offset_skins,0) + for i in xrange(0, self.num_skins): + self.skins[i].load(file) + #self.skins[i].dump() + + #load the texture coordinates + file.seek(self.offset_tex_coords,0) + for i in xrange(0, self.num_tex_coords): + self.tex_coords[i].load(file) + #self.tex_coords[i].dump() + + #load the face info + file.seek(self.offset_faces,0) + for i in xrange(0, self.num_faces): + self.faces[i].load(file) + #self.faces[i].dump() + + #load the frames + file.seek(self.offset_frames,0) + for i in xrange(0, self.num_frames): + self.frames[i].load(file) + #self.frames[i].dump() + for j in xrange(0,self.num_vertices): + self.frames[i].vertices[j].load(file) + #self.frames[i].vertices[j].dump() + return self + + def dump (self): + print "Header Information" + print "ident: ", self.ident + print "version: ", self.version + print "skin width: ", self.skin_width + print "skin height: ", self.skin_height + print "frame size: ", self.frame_size + print "number of skins: ", self.num_skins + print "number of texture coordinates: ", self.num_tex_coords + print "number of faces: ", self.num_faces + print "number of frames: ", self.num_frames + print "number of vertices: ", self.num_vertices + print "offset skins: ", self.offset_skins + print "offset texture coordinates: ", self.offset_tex_coords + print "offset faces: ", self.offset_faces + print "offset frames: ",self.offset_frames + print "" + +###################################################### +# Import functions +###################################################### +def load_textures(md2, texture_filename): + #did the user specify a texture they wanted to use? + if (texture_filename!="texture"): + if (Blender.sys.exists(texture_filename)): + mesh_image=Blender.Image.Load(texture_filename) + return mesh_image + else: + result=Blender.Draw.PupMenu("Cannot find texture: "+texture_filename+"-Continue?%t|OK") + if(result==1): + return -1 + #does the model have textures specified with it? + if int(md2.num_skins) > 0: + for i in xrange(0,md2.num_skins): + #md2.skins[i].dump() + if (Blender.sys.exists(md2.skins[i].name)): + mesh_image=Blender.Image.Load(md2.skins[i].name) + else: + result=Blender.Draw.PupMenu("Cannot find texture: "+md2.skins[i].name+"-Continue?%t|OK") + if(result==1): + return -1 + return mesh_image + else: + result=Blender.Draw.PupMenu("There will be no Texutre"+"-Continue?%t|OK") + if(result==1): + return -1 + + +def animate_md2(md2, mesh_obj): + ######### Animate the verts through keyframe animation + mesh=mesh_obj.getData() + for i in xrange(1, md2.num_frames): + #update the vertices + for j in xrange(0,md2.num_vertices): + x=(md2.frames[i].scale[0]*md2.frames[i].vertices[j].vertices[0]+md2.frames[i].translate[0])*g_scale.val + y=(md2.frames[i].scale[1]*md2.frames[i].vertices[j].vertices[1]+md2.frames[i].translate[1])*g_scale.val + z=(md2.frames[i].scale[2]*md2.frames[i].vertices[j].vertices[2]+md2.frames[i].translate[2])*g_scale.val + + #put the vertex in the right spot + mesh.verts[j].co[0]=y + mesh.verts[j].co[1]=-x + mesh.verts[j].co[2]=z + + mesh.update() + NMesh.PutRaw(mesh, mesh_obj.name) + #absolute keys, need to figure out how to get them working around the 100 frame limitation + mesh.insertKey(i,"absolute") + + #not really necissary, but I like playing with the frame counter + Blender.Set("curframe", i) + + +def load_md2 (md2_filename, texture_filename): + #read the file in + file=open(md2_filename,"rb") + md2=md2_obj() + md2.load(file) + #md2.dump() + file.close() + + ######### Creates a new mesh + mesh = NMesh.New() + + uv_coord=[] + uv_list=[] + + #load the textures to use later + #-1 if there is no texture to load + mesh_image=load_textures(md2, texture_filename) + + ######### Make the verts + DrawProgressBar(0.25,"Loading Vertex Data") + for i in xrange(0,md2.num_vertices): + #use the first frame for the mesh vertices + x=(md2.frames[0].scale[0]*md2.frames[0].vertices[i].vertices[0]+md2.frames[0].translate[0])*g_scale.val + y=(md2.frames[0].scale[1]*md2.frames[0].vertices[i].vertices[1]+md2.frames[0].translate[1])*g_scale.val + z=(md2.frames[0].scale[2]*md2.frames[0].vertices[i].vertices[2]+md2.frames[0].translate[2])*g_scale.val + vertex=NMesh.Vert(y,-x,z) + mesh.verts.append(vertex) + + ######## Make the UV list + DrawProgressBar(0.50,"Loading UV Data") + mesh.hasFaceUV(1) #turn on face UV coordinates for this mesh + for i in xrange(0, md2.num_tex_coords): + u=(float(md2.tex_coords[i].u)/float(md2.skin_width)) + v=(float(md2.tex_coords[i].v)/float(md2.skin_height)) + #for some reason quake2 texture maps are upside down, flip that + uv_coord=(u,1-v) + uv_list.append(uv_coord) + + ######### Make the faces + DrawProgressBar(0.75,"Loading Face Data") + for i in xrange(0,md2.num_faces): + face = NMesh.Face() + #draw the triangles in reverse order so they show up + face.v.append(mesh.verts[md2.faces[i].vertex_index[0]]) + face.v.append(mesh.verts[md2.faces[i].vertex_index[2]]) + face.v.append(mesh.verts[md2.faces[i].vertex_index[1]]) + #append the list of UV + #ditto in reverse order with the texture verts + face.uv.append(uv_list[md2.faces[i].texture_index[0]]) + face.uv.append(uv_list[md2.faces[i].texture_index[2]]) + face.uv.append(uv_list[md2.faces[i].texture_index[1]]) + + #set the texture that this face uses if it has one + if (mesh_image!=-1): + face.image=mesh_image + + #add the face + mesh.faces.append(face) + + mesh_obj=NMesh.PutRaw(mesh) + animate_md2(md2, mesh_obj) + DrawProgressBar(0.999,"Loading Animation Data") + + #locate the Object containing the mesh at the cursor location + cursor_pos=Blender.Window.GetCursorPos() + mesh_obj.setLocation(float(cursor_pos[0]),float(cursor_pos[1]),float(cursor_pos[2])) + DrawProgressBar (1.0, "Finished") + +#*********************************************** +# MAIN +#*********************************************** + +# Import globals +g_md2_filename=Create("model") +g_texture_filename=Create("texture") + +g_filename_search=Create("model") +g_texture_search=Create("texture") + +#Globals +g_scale=Create(1.0) + +# Events +EVENT_NOEVENT=1 +EVENT_LOAD_MD2=2 +EVENT_CHOOSE_FILENAME=3 +EVENT_CHOOSE_TEXTURE=4 +EVENT_SAVE_MD2=5 +EVENT_EXIT=100 + +###################################################### +# Callbacks for Window functions +###################################################### +def filename_callback(input_filename): + global g_md2_filename + g_md2_filename.val=input_filename + +def texture_callback(input_texture): + global g_texture_filename + g_texture_filename.val=input_texture + +###################################################### +# GUI Loader +###################################################### + + +def draw_gui(): + global g_scale + global g_md2_filename + global g_texture_filename + global EVENT_NOEVENT,EVENT_LOAD_MD2,EVENT_CHOOSE_FILENAME,EVENT_CHOOSE_TEXTURE,EVENT_EXIT + + ########## Titles + glClear(GL_COLOR_BUFFER_BIT) + glRasterPos2d(8, 125) + Text("MD2 loader") + + ######### Parameters GUI Buttons + g_md2_filename = String("MD2 file to load: ", EVENT_NOEVENT, 10, 55, 210, 18, + g_md2_filename.val, 255, "MD2 file to load") + ########## MD2 File Search Button + Button("Search",EVENT_CHOOSE_FILENAME,220,55,80,18) + + g_texture_filename = String("Texture file to load: ", EVENT_NOEVENT, 10, 35, 210, 18, + g_texture_filename.val, 255, "Texture file to load-overrides MD2 file") + ########## Texture Search Button + Button("Search",EVENT_CHOOSE_TEXTURE,220,35,80,18) + + ########## Scale slider-default is 1/8 which is a good scale for md2->blender + g_scale= Slider("Scale Factor: ", EVENT_NOEVENT, 10, 75, 210, 18, + 1.0, 0.001, 10.0, 1, "Scale factor for obj Model"); + + ######### Draw and Exit Buttons + Button("Load",EVENT_LOAD_MD2 , 10, 10, 80, 18) + Button("Exit",EVENT_EXIT , 170, 10, 80, 18) + +def event(evt, val): + if (evt == QKEY and not val): + Blender.Draw.Exit() + +def bevent(evt): + global g_md2_filename + global g_texture_filename + global EVENT_NOEVENT,EVENT_LOAD_MD2,EVENT_SAVE_MD2,EVENT_EXIT + + ######### Manages GUI events + if (evt==EVENT_EXIT): + Blender.Draw.Exit() + elif (evt==EVENT_CHOOSE_FILENAME): + FileSelector(filename_callback, "MD2 File Selection") + elif (evt==EVENT_CHOOSE_TEXTURE): + FileSelector(texture_callback, "Texture Selection") + elif (evt==EVENT_LOAD_MD2): + if (g_md2_filename.val == "model"): + Blender.Draw.Exit() + return + else: + load_md2(g_md2_filename.val, g_texture_filename.val) + Blender.Redraw() + Blender.Draw.Exit() + return + + +Register(draw_gui, event, bevent) diff --git a/release/scripts/mirror_bone_weights.py b/release/scripts/mirror_bone_weights.py new file mode 100644 index 00000000000..615a7ffb5ab --- /dev/null +++ b/release/scripts/mirror_bone_weights.py @@ -0,0 +1,218 @@ +#!BPY +""" +Name: 'Mirror Bone Weights' +Blender: 239 +Group: 'Mesh' +Submenu: '-x to +x' nxtopx +Submenu: '+x to -x' pxtonx +Tooltip: 'Mirror vertex group influences of a model' +""" + +__author__ = "Thomas Oppl" +__version__ = "5.12" +__url__ = "elysiun" +__email__ = "scripts" +__bpydoc__ = """\ +Description: + +This script copies vertex group influences from one half of a model to the +other half of it. + +Usage: + +- Select the model<br> +- Start the script (Object -> Scripts -> Mirror Bone Weights)<br> +- Use the "-x to +x" or the "+x to -x" submenu depending on which side should + be the source and which the destination.<br> + +Notes: + +- The model has to be in the center of the world along the x-axis.<br> +- The model has to be symmetrical along the x-axis.<br> +- You have to use the ".R" and ".L" suffix naming scheme for your vertex groups.<br> + +""" + +#------------------------------------------------------------ +# Mirror Bone Weights - (c) 2005 thomas oppl - toppl@fh-sbg.ac.at +#------------------------------------------------------------ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# ***** END GPL LICENCE BLOCK ***** +#------------------------------------------------------------ + + + + + +from Blender import NMesh, Object, Draw, sys, Types, Window + + + + + +threshold = 0.001 + +################################################################################ +def mirror(mode): + print + print "mirror bone weights: %s" % (mode) + print "threshold: %.6f" % (threshold) + + objects = Object.GetSelected() + if not objects: + Draw.PupMenu("Error: no object selected!") + print "no object selected!" + return + mesh = objects[0].getData() + if type(mesh) != Types.NMeshType: + Draw.PupMenu("Error: object must be a mesh!") + print "object is no mesh!" + return + + # nmesh.getvertexinfluences function seems to be broken so i create a dictionary instead + Window.WaitCursor(1) + time = sys.time() + in_editmode = Window.EditMode() + if in_editmode: Window.EditMode(0) + vertexinfluences = {} + for i in range(len(mesh.verts)): + vertexinfluences[i] = [] + for groupname in mesh.getVertGroupNames(): + for vertex in mesh.getVertsFromGroup(groupname, 1): + index, weight = vertex[0], vertex[1] + vertexinfluences[index].append((groupname, weight)) + influencestime = sys.time() - time + print "influence dictionary generated in %.6f seconds!" % (influencestime) + + # generate binary tree to speed up looking for opposite vertex + time = sys.time() + tree = c_tree(mesh.verts) + treetime = sys.time() - time + print "binary tree generated in %.6f seconds!" % (treetime) + + # mirror vertex group influences + time = sys.time() + if mode == "-x to +x": + verticeshalf = [v for v in mesh.verts if v.co[0] < 0] + else: + verticeshalf = [v for v in mesh.verts if v.co[0] > 0] + i = 0 + for vertex in verticeshalf: + oppositeposition = (-vertex.co[0], vertex.co[1], vertex.co[2]) + foundvertex = [] + tree.findvertex(oppositeposition, foundvertex, threshold) + if foundvertex: + oppositevertex = foundvertex[0] + # remove all influences from opposite vertex + for influence in vertexinfluences[oppositevertex.index]: + mesh.removeVertsFromGroup(influence[0], [oppositevertex.index]) + # copy influences to opposite vertex + for influence in vertexinfluences[vertex.index]: + name = influence[0] + if name[-2:] == ".R": + name = name[:-2] + ".L" + elif name[-2:] == ".L": + name = name[:-2] + ".R" + if name not in mesh.getVertGroupNames(): # create opposite group if it doesn't exist + mesh.addVertGroup(name) + mesh.assignVertsToGroup(name, [oppositevertex.index], influence[1], "add") + i += 1 + mirrortime = sys.time() - time + print "%d vertices mirrored in %.6f seconds!" % (i, mirrortime) + + # done! + print "done in %.6f seconds total!" % (influencestime + treetime + mirrortime) + if in_editmode: Window.EditMode(1) + Window.WaitCursor(0) + + + + +################################################################################ +NODE_VERTEX_LIMIT = 50 + +class c_boundingbox: + def __init__(self, vertices): + self.min_x = self.max_x = vertices[0].co[0] + self.min_y = self.max_y = vertices[0].co[1] + self.min_z = self.max_z = vertices[0].co[2] + for vertex in vertices: + self.min_x = min(self.min_x, vertex.co[0]) + self.min_y = min(self.min_y, vertex.co[1]) + self.min_z = min(self.min_z, vertex.co[2]) + self.max_x = max(self.max_x, vertex.co[0]) + self.max_y = max(self.max_y, vertex.co[1]) + self.max_z = max(self.max_z, vertex.co[2]) + self.dim_x = self.max_x - self.min_x + self.dim_y = self.max_y - self.min_y + self.dim_z = self.max_z - self.min_z + self.splitaxis = [self.dim_x, self.dim_y, self.dim_z].index(max(self.dim_x, self.dim_y, self.dim_z)) + self.center_x = self.max_x - (self.dim_x / 2.0) + self.center_y = self.max_y - (self.dim_y / 2.0) + self.center_z = self.max_z - (self.dim_z / 2.0) + self.splitcenter = [self.center_x, self.center_y, self.center_z][self.splitaxis] + def __str__(self): + return "min: %.3f %.3f %.3f max: %.3f %.3f %.3f dim: %.3f %.3f %.3f" %\ + (self.min_x, self.min_y, self.min_z, + self.max_x, self.max_y, self.max_z, + self.dim_x, self.dim_y, self.dim_z) + def isinside(self, position, threshold): + return (position[0] <= self.max_x + threshold and position[1] <= self.max_y + threshold and \ + position[2] <= self.max_z + threshold and position[0] >= self.min_x - threshold and \ + position[1] >= self.min_y - threshold and position[2] >= self.min_z - threshold) + +class c_tree: + def __init__(self, vertices, level = 0): + self.level = level + self.children = [] + self.vertices = [] + self.boundingbox = c_boundingbox(vertices) + splitaxis = self.boundingbox.splitaxis + splitcenter = self.boundingbox.splitcenter + if len(vertices) > NODE_VERTEX_LIMIT: + self.children.append(c_tree( + [v for v in vertices if v.co[splitaxis] > splitcenter], self.level + 1)) + self.children.append(c_tree( + [v for v in vertices if v.co[splitaxis] <= splitcenter], self.level + 1)) + else: # leaf node + self.vertices = vertices + def __str__(self): + s = " " * self.level + "-node %d\n" % (len(self.vertices)) + for child in self.children: + s += str(child) + return s + def findvertex(self, position, foundvertex, threshold): + if self.boundingbox.isinside(position, threshold): + if self.children: + for child in self.children: + child.findvertex(position, foundvertex, threshold) + else: # node found + for vertex in self.vertices: + v, p, t = vertex.co, position, threshold + if abs(v[0] - p[0]) < t and abs(v[1] - p[1]) < t and abs(v[2] - p[2]) < t: # vertex found + foundvertex.append(vertex) + + + + + +################################################################################ +if __script__["arg"] == "nxtopx": + mirror("-x to +x") +if __script__["arg"] == "pxtonx": + mirror("+x to -x") diff --git a/release/scripts/widgetwizard.py b/release/scripts/widgetwizard.py new file mode 100644 index 00000000000..8d3dda4677e --- /dev/null +++ b/release/scripts/widgetwizard.py @@ -0,0 +1,913 @@ +#!BPY + +""" +Name: 'Shape Widget Wizard' +Blender: 238 +Group: 'Animation' +Tip: 'Adds Widgets for Driven Shapes' +""" + +__author__ = ["Johnny Matthews (guitargeek)"] +__url__ = ("blender", "elysiun") +__version__ = "0.0.9 12/15/05" + +__bpydoc__ = """\ +"Shape Widget Wizard" creates objects that drive shape channels. + +Explanation: + +Shapes define morph targets and sometimes it is helpful to animate with a GUI +control panel of widgets. This script lets you define several different types +of controls that (depending on the type) control 1 to 4 shapes with a single +controller. + +Usage: + +1. Click where you want the widget to go<br> +2. Highlight the object that has shapes<br> +3. Run the script<br> +4. Choose the type of widget (there are next and back buttons if you pick the wrong kind)<br> +5. Click next and choose what shapes go where on the widget<br> +6. Choose a display name for the widget<br> +7. Click finish + +The widget is added and you are returned to the first screen for adding another widget. + +""" + +################################################################### +# # +# Shape Widget Wizard # +# # +# all versions (C) December 2005 Johnny Matthews (guitargeek) # +# # +# Released under the GPL # +# # +# Works in Blender 2.4 and higher # +# # +# This script can be found online at: # +# http://guitargeek.superihost.com/widgetmaker # +# # +# email: johnny.matthews@gmail.com # +################################################################### +# History # +# 0.9 # +# Added Name Objects # +# 0.81 # +# Added Single Shape Toggle # +# # +# 0.8 # +# Controller is Transform Locked and can only move # +# in appropriate directions # +# # +# 0.7 # +# Controller is named the same as the range + ".ctrl" # +# # +################################################################### + +import Blender +from Blender import Mesh,Object,Material,Window,IpoCurve,Ipo,Text3d +from Blender.BGL import * +from Blender.Draw import * +print "----------------------" + +SHAPE1_ONE_MONE = 1 +SHAPE1_ONE_ZERO = 2 +SHAPE1_ZERO_MONE = 3 +SHAPE1_TOGGLE = 12 +SHAPE2_EXCLUSIVE = 4 +SHAPE2_V = 5 +SHAPE2_T = 6 +SHAPE2_INVT = 7 +SHAPE2_PLUS = 8 +SHAPE3_T = 9 +SHAPE3_INVT = 10 +SHAPE4_X = 11 + + +stage = 1 +numshapes = Create(1) +widmenu = Create(1) +rangename = Create("Range") +shapes = [Create(0),Create(0),Create(0),Create(0)] +drawtype = 0 + + +#get rid of an ipo curve by deleting all its points +def delCurve(ipo): + while len(ipo.getPoints()) > 0: + ipo.delBezier(0) + ipo.recalc() + +#if a given ipocurve is not there create it, otherwise get it +def verifyIpocurve(ky,index): + ipo = ky.ipo + if ipo == None: + nip = Ipo.New("Key","keyipo") + ky.ipo = nip + ipo = ky.ipo + idx = "Key " + str(index) + crv = ipo.getCurve(index) + if crv == None: + crv = ipo.addCurve(idx) + crv.setInterpolation("Linear") + return crv + +# Add the Drivers and Curves +def setupDrivers(ob,ctrl,type): + global shapes + me = ob.getData() + ky = me.getKey() + + if type in [SHAPE1_ONE_MONE,SHAPE1_ONE_ZERO,SHAPE1_ZERO_MONE]: + ctrl.protectFlags = int("111111011",2) + ipo = verifyIpocurve(ky,shapes[0].val) + ipo.driver = 1 + ipo.driverObject = ctrl + ipo.driverChannel = IpoCurve.LOC_Z + ipo.recalc() + + delCurve(ipo) + if type == 1: + ipo.addBezier((-1,-1)) + ipo.addBezier((0,0)) + ipo.addBezier((1,1)) + if type == 2: + ipo.addBezier((0,0)) + ipo.addBezier((1,1)) + if type == 3: + ipo.addBezier((-1,-1)) + ipo.addBezier((0,0)) + ipo.recalc() + + if type == SHAPE1_TOGGLE: + ctrl.protectFlags = int("111111011",2) + ipo = verifyIpocurve(ky,shapes[0].val) + ipo.driver = 1 + ipo.driverObject = ctrl + ipo.driverChannel = IpoCurve.LOC_Z + ipo.recalc() + delCurve(ipo) + ipo.addBezier((0,0)) + ipo.addBezier((0.5,0)) + ipo.addBezier((0.500001,1)) + ipo.addBezier((1,1)) + ipo.recalc() + + if type == SHAPE2_EXCLUSIVE: + ctrl.protectFlags = int("111111011",2) + ipo = verifyIpocurve(ky,shapes[0].val) + ipo.driver = 1 + ipo.driverObject = ctrl + ipo.driverChannel = IpoCurve.LOC_Z + ipo.recalc() + delCurve(ipo) + ipo.addBezier((0,0)) + ipo.addBezier((1,1)) + ipo.recalc() + + ipo2 = verifyIpocurve(ky,shapes[1].val) + ipo2.driver = 1 + ipo2.driverObject = ctrl + ipo2.driverChannel = IpoCurve.LOC_Z + ipo2.recalc() + delCurve(ipo2) + ipo2.addBezier((-1,1)) + ipo2.addBezier((0,0)) + ipo2.recalc() + + if type == SHAPE2_T: + ctrl.protectFlags = int("111111010",2) + ipo = verifyIpocurve(ky,shapes[0].val) + ipo.driver = 1 + ipo.driverObject = ctrl + ipo.driverChannel = IpoCurve.LOC_Z + ipo.recalc() + delCurve(ipo) + ipo.addBezier((-1,-1)) + ipo.addBezier((0,0)) + ipo.recalc() + + ipo2 = verifyIpocurve(ky,shapes[1].val) + ipo2.driver = 1 + ipo2.driverObject = ctrl + ipo2.driverChannel = IpoCurve.LOC_X + ipo2.recalc() + delCurve(ipo2) + ipo2.addBezier((-1,-1)) + ipo2.addBezier((1,1)) + ipo2.recalc() + + if type == SHAPE2_INVT: + ctrl.protectFlags = int("111111010",2) + ipo = verifyIpocurve(ky,shapes[0].val) + ipo.driver = 1 + ipo.driverObject = ctrl + ipo.driverChannel = IpoCurve.LOC_Z + ipo.recalc() + delCurve(ipo) + ipo.addBezier((0,0)) + ipo.addBezier((1,1)) + ipo.recalc() + + ipo2 = verifyIpocurve(ky,shapes[1].val) + ipo2.driver = 1 + ipo2.driverObject = ctrl + ipo2.driverChannel = IpoCurve.LOC_X + ipo2.recalc() + delCurve(ipo2) + ipo2.addBezier((-1,-1)) + ipo2.addBezier((1,1)) + ipo2.recalc() + + if type == SHAPE2_PLUS: + ctrl.protectFlags = int("111111010",2) + ipo = verifyIpocurve(ky,shapes[0].val) + ipo.driver = 1 + ipo.driverObject = ctrl + ipo.driverChannel = IpoCurve.LOC_Z + ipo.recalc() + delCurve(ipo) + ipo.addBezier((-1,-1)) + ipo.addBezier((1,1)) + ipo.recalc() + + ipo2 = verifyIpocurve(ky,shapes[1].val) + ipo2.driver = 1 + ipo2.driverObject = ctrl + ipo2.driverChannel = IpoCurve.LOC_X + ipo2.recalc() + delCurve(ipo2) + ipo2.addBezier((-1,-1)) + ipo2.addBezier((1,1)) + ipo2.recalc() + + if type == SHAPE2_V: # 2 Shape Mix + ctrl.protectFlags = int("111111010",2) + ipo = verifyIpocurve(ky,shapes[0].val) + ipo.driver = 1 + ipo.driverObject = ctrl + ipo.driverChannel = IpoCurve.LOC_Z + delCurve(ipo) + ipo.addBezier((0,0)) + ipo.addBezier((1,1)) + ipo.recalc() + + ipo2 = verifyIpocurve(ky,shapes[1].val) + ipo2.driver = 1 + ipo2.driverObject = ctrl + ipo2.driverChannel = IpoCurve.LOC_X + delCurve(ipo2) + ipo2.addBezier((0,0)) + ipo2.addBezier((1,1)) + ipo2.recalc() + + + if type == SHAPE3_INVT: + ctrl.protectFlags = int("111111010",2) + ipo = verifyIpocurve(ky,shapes[0].val) + ipo.driver = 1 + ipo.driverObject = ctrl + ipo.driverChannel = IpoCurve.LOC_Z + ipo.recalc() + delCurve(ipo) + ipo.addBezier((0,0)) + ipo.addBezier((1,1)) + ipo.recalc() + + ipo2 = verifyIpocurve(ky,shapes[1].val) + ipo2.driver = 1 + ipo2.driverObject = ctrl + ipo2.driverChannel = IpoCurve.LOC_X + ipo2.recalc() + delCurve(ipo2) + ipo2.addBezier((-1,1)) + ipo2.addBezier((0,0)) + ipo2.recalc() + + ipo2 = verifyIpocurve(ky,shapes[2].val) + ipo2.driver = 1 + ipo2.driverObject = ctrl + ipo2.driverChannel = IpoCurve.LOC_X + ipo2.recalc() + delCurve(ipo2) + ipo2.addBezier((0,0)) + ipo2.addBezier((1,1)) + ipo2.recalc() + + if type == SHAPE3_T: + ctrl.protectFlags = int("111111010",2) + ipo = verifyIpocurve(ky,shapes[0].val) + ipo.driver = 1 + ipo.driverObject = ctrl + ipo.driverChannel = IpoCurve.LOC_Z + ipo.recalc() + delCurve(ipo) + ipo.addBezier((-1,-1)) + ipo.addBezier((0,0)) + ipo.recalc() + + ipo2 = verifyIpocurve(ky,shapes[1].val) + ipo2.driver = 1 + ipo2.driverObject = ctrl + ipo2.driverChannel = IpoCurve.LOC_X + ipo2.recalc() + delCurve(ipo2) + ipo2.addBezier((-1,1)) + ipo2.addBezier((0,0)) + ipo2.recalc() + + ipo2 = verifyIpocurve(ky,shapes[2].val) + ipo2.driver = 1 + ipo2.driverObject = ctrl + ipo2.driverChannel = IpoCurve.LOC_X + ipo2.recalc() + delCurve(ipo2) + ipo2.addBezier((0,0)) + ipo2.addBezier((1,1)) + ipo2.recalc() + + if type == SHAPE4_X: + ctrl.protectFlags = int("111111010",2) + ipo = verifyIpocurve(ky,shapes[0].val) + ipo.driver = 1 + ipo.driverObject = ctrl + ipo.driverChannel = IpoCurve.LOC_Z + delCurve(ipo) + ipo.addBezier((0,0)) + ipo.addBezier((1,1)) + ipo.recalc() + + ipo2 = verifyIpocurve(ky,shapes[1].val) + ipo2.driver = 1 + ipo2.driverObject = ctrl + ipo2.driverChannel = IpoCurve.LOC_X + delCurve(ipo2) + ipo2.addBezier((0,0)) + ipo2.addBezier((1,1)) + ipo2.recalc() + + ipo3 = verifyIpocurve(ky,shapes[2].val) + ipo3.driver = 1 + ipo3.driverObject = ctrl + ipo3.driverChannel = IpoCurve.LOC_X + delCurve(ipo3) + ipo3.addBezier((-1,1)) + ipo3.addBezier((0,0)) + ipo3.recalc() + + ipo4 = verifyIpocurve(ky,shapes[3].val) + ipo4.driver = 1 + ipo4.driverObject = ctrl + ipo4.driverChannel = IpoCurve.LOC_Z + delCurve(ipo4) + ipo4.addBezier((-1,1)) + ipo4.addBezier((0,0)) + ipo4.recalc() + +#The Main Call to Build the Widget + +def build(type): + global shapes,widmenu,rangename + sce = Blender.Scene.getCurrent() + loc = Window.GetCursorPos() + range = makeRange(type,rangename.val) + controller = makeController(rangename.val) + text = makeText(rangename.val) + + sce.link(range) + sce.link(controller) + sce.link(text) + + range.setLocation(loc) + controller.setLocation(loc) + text.setLocation(loc) + + range.makeParent([controller],1) + range.makeParent([text],0) + + sce.update() + + ob = Object.GetSelected()[0] + setupDrivers(ob,controller,widmenu.val) + +#Create the text + +def makeText(name): + ob = Object.New("Text",name+".name") + txt = Text3d.New(name+".name") + txt.setDrawMode(Text3d.DRAW3D) + txt.setAlignment(Text3d.MIDDLE) + txt.setText(name) + ob.link(txt) + ob.setEuler(3.14159/2,0,0) + return ob + + +#Create the mesh controller + +def makeController(name): + ob = Object.New("Mesh",name+".ctrl") + me = Mesh.New(name+".ctrl") + + me.verts.extend(-0.15,0, 0) + me.verts.extend( 0,0, 0.15) + me.verts.extend( 0.15,0, 0) + me.verts.extend( 0,0,-0.15) + v = me.verts + c = [(v[0],v[1],v[2],v[3])] + me.edges.extend(c) + ob.link(me) + return ob + +#Create the mesh range + +def makeRange(type,name): + ob = Object.New("Mesh",name) + #ob.setDrawMode(8) # Draw Name + me = Mesh.New(name) + + l=[] + + if type == SHAPE1_ONE_ZERO: + me.verts.extend(-0.15,0,0) + me.verts.extend( 0.15,0,0) + me.verts.extend(-0.15,0,1) + me.verts.extend( 0.15,0,1) + me.verts.extend(-0.25,0,.1) + me.verts.extend(-0.25,0,-.10) + me.verts.extend(0.25,0,.1) + me.verts.extend(0.25,0,-0.10) + v = me.verts + l = [(v[0],v[1],v[3],v[2]),(v[4],v[5],v[0]),(v[6],v[7],v[1])] + me.edges.extend(l) + ob.link(me) + + elif type == SHAPE1_TOGGLE: + me.verts.extend(-0.15,0,-0.5) + me.verts.extend( 0.15,0,-0.5) + me.verts.extend( 0.15,0, 0.5) + me.verts.extend(-0.15,0, 0.5) + me.verts.extend(-0.15,0, 1.5) + me.verts.extend( 0.15,0, 1.5) + v = me.verts + l = [(v[0],v[1],v[2],v[3]),(v[3],v[4],v[5],v[2])] + me.edges.extend(l) + ob.link(me) + + elif type == SHAPE1_ZERO_MONE: + me.verts.extend(-0.15,0,0) + me.verts.extend( 0.15,0,0) + me.verts.extend(-0.15,0,-1) + me.verts.extend( 0.15,0,-1) + me.verts.extend(-0.25,0,.1) + me.verts.extend(-0.25,0,-.10) + me.verts.extend(0.25,0,.1) + me.verts.extend(0.25,0,-0.10) + v = me.verts + l = [(v[0],v[1],v[3],v[2]),(v[4],v[5],v[0]),(v[6],v[7],v[1])] + me.edges.extend(l) + ob.link(me) + + elif type in [SHAPE1_ONE_MONE,SHAPE2_EXCLUSIVE]: + me.verts.extend(-0.15,0,-1) + me.verts.extend( 0.15,0,-1) + me.verts.extend(-0.15,0,1) + me.verts.extend( 0.15,0,1) + me.verts.extend(-0.25,0,.1) + me.verts.extend(-0.25,0,-.10) + me.verts.extend(0.25,0,.1) + me.verts.extend(0.25,0,-0.10) + me.verts.extend(-0.15,0,0) + me.verts.extend( 0.15,0,0) + + v = me.verts + l = [(v[0],v[1],v[3],v[2]),(v[4],v[5],v[8]),(v[6],v[7],v[9])] + me.edges.extend(l) + ob.link(me) + + elif type == SHAPE2_T: + me.verts.extend(-1,0,0) + me.verts.extend( 1,0,0) + me.verts.extend( 1,0,-1) + me.verts.extend(-1,0,-1) + + v = me.verts + l = [(v[0],v[1],v[2],v[3])] + me.edges.extend(l) + ob.link(me) + + elif type == SHAPE2_INVT: + me.verts.extend(-1,0,0) + me.verts.extend( 1,0,0) + me.verts.extend( 1,0,1) + me.verts.extend(-1,0,1) + + v = me.verts + l = [(v[0],v[1],v[2],v[3])] + me.edges.extend(l) + ob.link(me) + + elif type == SHAPE2_PLUS: + me.verts.extend(-1,0,-1) + me.verts.extend( 1,0,-1) + me.verts.extend( 1,0,1) + me.verts.extend(-1,0,1) + + v = me.verts + l = [(v[0],v[1],v[2],v[3])] + me.edges.extend(l) + ob.link(me) + + elif type == SHAPE2_V: + me.verts.extend(0,0,0) + me.verts.extend(1,0,0) + me.verts.extend(1,0,1) + me.verts.extend(0,0,1) + v = me.verts + l = [(v[0],v[1],v[2],v[3])] + me.edges.extend(l) + ob.link(me) + ob.setEuler(0,-0.78539,0) + + elif type == SHAPE3_INVT: + me.verts.extend(-1,0,0) + me.verts.extend( 1,0,0) + me.verts.extend( 1,0,1) + me.verts.extend(-1,0,1) + + v = me.verts + l = [(v[0],v[1],v[2],v[3])] + me.edges.extend(l) + ob.link(me) + + elif type == SHAPE3_T: + me.verts.extend(-1,0,0) + me.verts.extend( 1,0,0) + me.verts.extend( 1,0,-1) + me.verts.extend(-1,0,-1) + + v = me.verts + l = [(v[0],v[1],v[2],v[3])] + me.edges.extend(l) + ob.link(me) + + + elif type == SHAPE4_X: + me.verts.extend(0,0,-1) + me.verts.extend(1,0,-1) + me.verts.extend(1,0,0) + me.verts.extend(1,0,1) + me.verts.extend(0,0,1) + me.verts.extend(-1,0,1) + me.verts.extend(-1,0,0) + me.verts.extend(-1,0,-1) + v = me.verts + l = [(v[0],v[1]),(v[1],v[2]),(v[2],v[3]),(v[3],v[4]),(v[4],v[5]),(v[5],v[6]),(v[6],v[7]),(v[7],v[0])] + me.edges.extend(l) + ob.link(me) + ob.setEuler(0,-0.78539,0) + + return ob + + +def create(): + main() + +####################### gui ###################### + + +EVENT_NONE = 1 +EVENT_EXIT = 100 +EVENT_WIDGET_MENU = 101 +EVENT_NEXT = 102 +EVENT_BACK = 103 + +#get the list of shapes from the selected object + +def shapeMenuText(): + if len(Object.GetSelected()) == 0: + return "" + ob = Object.GetSelected()[0] + me = ob.getData() + key= me.getKey() + if key == None: + return "" + blocks = key.getBlocks() + menu = "Choose Shape %t|" + for n in range(len(blocks)): + menu = menu + blocks[n].name + " %x" + str(n) + "|" + return menu + + +#draw the widget for the gui + +def drawWidget(type): + global shapes + if type == SHAPE1_ONE_MONE:# 1 to -1 Single Shape + glBegin(GL_LINE_STRIP) + glVertex2i(150,50) + glVertex2i(170,50) + glVertex2i(170,150) + glVertex2i(150,150) + glVertex2i(150,50) + glEnd() + glBegin(GL_LINE_STRIP) + glVertex2i(140,100) + glVertex2i(190,100) + glEnd() + glRasterPos2d(180,140) + Text("1","normal") + glRasterPos2d(180,60) + Text("-1","normal") + shapes[0] = Menu(shapeMenuText(), EVENT_NONE, 190, 100, 100, 18, shapes[0].val, "Choose Shape.") + elif type == SHAPE1_TOGGLE:# Toggle Single Shape + glBegin(GL_LINE_STRIP) + glVertex2i(150,50) + glVertex2i(170,50) + glVertex2i(170,100) + glVertex2i(150,100) + glVertex2i(150,50) + glEnd() + glBegin(GL_LINE_STRIP) + glVertex2i(170,100) + glVertex2i(170,150) + glVertex2i(150,150) + glVertex2i(150,100) + glEnd() + glRasterPos2d(180,140) + Text("On","normal") + glRasterPos2d(180,60) + Text("Off","normal") + shapes[0] = Menu(shapeMenuText(), EVENT_NONE, 190, 100, 100, 18, shapes[0].val, "Choose Shape.") + elif type == SHAPE1_ONE_ZERO: # 1 to 0 Single Shape + glBegin(GL_LINE_STRIP) + glVertex2i(150,50) + glVertex2i(170,50) + glVertex2i(170,150) + glVertex2i(150,150) + glVertex2i(150,50) + glEnd() + glBegin(GL_LINE_STRIP) + glVertex2i(140,50) + glVertex2i(190,50) + glEnd() + glRasterPos2d(180,140) + Text("1","normal") + glRasterPos2d(180,60) + Text("0","normal") + shapes[0] = Menu(shapeMenuText(), EVENT_NONE, 190, 100, 100, 18, shapes[0].val, "Choose Shape.") + elif type == SHAPE1_ZERO_MONE: + glBegin(GL_LINE_STRIP) + glVertex2i(150,50) + glVertex2i(170,50) + glVertex2i(170,150) + glVertex2i(150,150) + glVertex2i(150,50) + glEnd() + glBegin(GL_LINE_STRIP) + glVertex2i(140,150) + glVertex2i(190,150) + glEnd() + glRasterPos2d(180,140) + Text("0","normal") + glRasterPos2d(180,60) + Text("-1","normal") + shapes[0] = Menu(shapeMenuText(), EVENT_NONE, 190, 100, 100, 18, shapes[0].val, "Choose Shape.") + elif type == SHAPE2_EXCLUSIVE: + glBegin(GL_LINE_STRIP) + glVertex2i(150,50) + glVertex2i(170,50) + glVertex2i(170,150) + glVertex2i(150,150) + glVertex2i(150,50) + glEnd() + glBegin(GL_LINE_STRIP) + glVertex2i(140,100) + glVertex2i(190,100) + glEnd() + glRasterPos2d(180,140) + Text("1","normal") + glRasterPos2d(180,60) + Text("1","normal") + shapes[0] = Menu(shapeMenuText(), EVENT_NONE, 195, 135, 100, 18, shapes[0].val, "Choose Shape 1.") + shapes[1] = Menu(shapeMenuText(), EVENT_NONE, 195, 52, 100, 18, shapes[1].val, "Choose Shape 2.") + elif type == SHAPE2_T: + glBegin(GL_LINE_STRIP) + glVertex2i(150,75) + glVertex2i(250,75) + glVertex2i(250,125) + glVertex2i(150,125) + glVertex2i(150,75) + glEnd() + glBegin(GL_LINE_STRIP) + glVertex2i(140,125) + glVertex2i(260,125) + glEnd() + glRasterPos2d(200,140) + Text("0","normal") + glRasterPos2d(200,60) + Text("-1","normal") + glRasterPos2d(250,140) + Text("1","normal") + glRasterPos2d(150,140) + Text("-1","normal") + shapes[0] = Menu(shapeMenuText(), EVENT_NONE, 220, 52, 100, 18, shapes[0].val, "Choose Shape 1.") + shapes[1] = Menu(shapeMenuText(), EVENT_NONE, 260, 135, 100, 18, shapes[1].val, "Choose Shape 2.") + elif type == SHAPE2_INVT: + glBegin(GL_LINE_STRIP) + glVertex2i(150,75) + glVertex2i(250,75) + glVertex2i(250,125) + glVertex2i(150,125) + glVertex2i(150,75) + glEnd() + glBegin(GL_LINE_STRIP) + glVertex2i(140,75) + glVertex2i(260,75) + glEnd() + glRasterPos2d(200,60) + Text("0","normal") + glRasterPos2d(200,140) + Text("1","normal") + glRasterPos2d(250,60) + Text("1","normal") + glRasterPos2d(150,60) + Text("-1","normal") + shapes[0] = Menu(shapeMenuText(), EVENT_NONE, 220, 135, 100, 18, shapes[0].val, "Choose Shape 1.") + shapes[1] = Menu(shapeMenuText(), EVENT_NONE, 260, 52, 100, 18, shapes[1].val, "Choose Shape 2.") + elif type == SHAPE2_PLUS: + glBegin(GL_LINE_STRIP) + glVertex2i(150,50) + glVertex2i(250,50) + glVertex2i(250,150) + glVertex2i(150,150) + glVertex2i(150,50) + glEnd() + glBegin(GL_LINE_STRIP) + glVertex2i(140,100) + glVertex2i(260,100) + glEnd() + glRasterPos2d(200,105) + Text("0","normal") + glRasterPos2d(200,140) + Text("1","normal") + glRasterPos2d(200,55) + Text("-1","normal") + glRasterPos2d(250,105) + Text("1","normal") + glRasterPos2d(150,105) + Text("-1","normal") + shapes[0] = Menu(shapeMenuText(), EVENT_NONE, 220, 155, 100, 18, shapes[0].val, "Choose Shape 1.") + shapes[1] = Menu(shapeMenuText(), EVENT_NONE, 260, 100, 100, 18, shapes[1].val, "Choose Shape 2.") + elif type == SHAPE2_V: + glBegin(GL_LINE_STRIP) + glVertex2i(150,70) + glVertex2i(185,105) + glVertex2i(150,141) + glVertex2i(115,105) + glVertex2i(150,70) + glEnd() + glRasterPos2d(110,105) + Text("1","normal") + glRasterPos2d(190,105) + Text("1","normal") + glRasterPos2d(150,80) + Text("0","normal") + shapes[0] = Menu(shapeMenuText(), EVENT_NONE, 20, 125, 100, 18, shapes[0].val, "Choose Shape 1.") + shapes[1] = Menu(shapeMenuText(), EVENT_NONE, 195, 125, 100, 18, shapes[1].val, "Choose Shape 2.") + + + + elif type == SHAPE3_T: + glBegin(GL_LINE_STRIP) + glVertex2i(150,75) + glVertex2i(250,75) + glVertex2i(250,125) + glVertex2i(150,125) + glVertex2i(150,75) + glEnd() + glBegin(GL_LINE_STRIP) + glVertex2i(140,125) + glVertex2i(260,125) + glEnd() + glRasterPos2d(200,140) + Text("0","normal") + glRasterPos2d(200,60) + Text("-1","normal") + glRasterPos2d(250,140) + Text("1","normal") + glRasterPos2d(150,140) + Text("1","normal") + shapes[0] = Menu(shapeMenuText(), EVENT_NONE, 220, 52, 100, 18, shapes[0].val, "Choose Shape 1.") + shapes[1] = Menu(shapeMenuText(), EVENT_NONE, 45, 135, 100, 18, shapes[1].val, "Choose Shape 2.") + shapes[2] = Menu(shapeMenuText(), EVENT_NONE, 260, 135, 100, 18, shapes[2].val, "Choose Shape 3.") + elif type == SHAPE3_INVT: + glBegin(GL_LINE_STRIP) + glVertex2i(150,75) + glVertex2i(250,75) + glVertex2i(250,125) + glVertex2i(150,125) + glVertex2i(150,75) + glEnd() + glBegin(GL_LINE_STRIP) + glVertex2i(140,75) + glVertex2i(260,75) + glEnd() + glRasterPos2d(200,60) + Text("0","normal") + glRasterPos2d(200,140) + Text("1","normal") + glRasterPos2d(250,60) + Text("1","normal") + glRasterPos2d(150,60) + Text("1","normal") + shapes[0] = Menu(shapeMenuText(), EVENT_NONE, 220, 135, 100, 18, shapes[0].val, "Choose Shape 1.") + shapes[1] = Menu(shapeMenuText(), EVENT_NONE, 45, 52, 100, 18, shapes[1].val, "Choose Shape 2.") + shapes[2] = Menu(shapeMenuText(), EVENT_NONE, 260, 52, 100, 18, shapes[2].val, "Choose Shape 3.") + + + elif type == SHAPE4_X: + glBegin(GL_LINE_STRIP) + glVertex2i(150,70) + glVertex2i(185,105) + glVertex2i(150,141) + glVertex2i(115,105) + glVertex2i(150,70) + glEnd() + glRasterPos2d(120,125) + Text("1","normal") + glRasterPos2d(180,125) + Text("1","normal") + glRasterPos2d(120,80) + Text("1","normal") + glRasterPos2d(180,80) + Text("1","normal") + + glRasterPos2d(145,105) + Text("0","normal") + shapes[0] = Menu(shapeMenuText(), EVENT_NONE, 10, 125, 100, 18, shapes[0].val, "Choose Shape 1.") + shapes[1] = Menu(shapeMenuText(), EVENT_NONE, 195, 125, 100, 18, shapes[1].val, "Choose Shape 2.") + shapes[2] = Menu(shapeMenuText(), EVENT_NONE, 10, 60, 100, 18, shapes[2].val, "Choose Shape 3.") + shapes[3] = Menu(shapeMenuText(), EVENT_NONE, 195, 60, 100, 18, shapes[3].val, "Choose Shape 4.") + +#the gui callback + +def draw(): + global widmenu,numshapes,stage,type, shapes,rangename + glRasterPos2d(5,200) + Text("Shape Widget Wizard","large") + PushButton("Quit", EVENT_EXIT, 5, 5, 50, 18) + + if stage == 1: + name = "Choose Widget Type %t|\ +1 Shape: 1 / -1 %x" +str(SHAPE1_ONE_MONE) +"|\ +1 Shape: 1,0 %x" +str(SHAPE1_ONE_ZERO) +"|\ +1 Shape: 0,-1 %x" +str(SHAPE1_ZERO_MONE)+"|\ +1 Shape: Toggle %x" +str(SHAPE1_TOGGLE) +"|\ +2 Shape Exclusive %x"+str(SHAPE2_EXCLUSIVE)+"|\ +2 Shape - V %x" +str(SHAPE2_V) +"|\ +2 Shape - T %x" +str(SHAPE2_T) +"|\ +2 Shape - Inv T %x" +str(SHAPE2_INVT) +"|\ +2 Shape - + %x" +str(SHAPE2_PLUS) +"|\ +3 Shape - T %x" +str(SHAPE3_T) +"|\ +3 Shape - Inv T%x" +str(SHAPE3_INVT) +"|\ +4 Shape - Mix %x" +str(SHAPE4_X) + widmenu = Menu(name, EVENT_NONE, 5, 120, 200, 40, widmenu.val, "Choose Widget Type.") + PushButton("Next", EVENT_NEXT, 5, 25, 50, 18) + + elif stage == 2: + glRasterPos2d(60,140) + rangename = String("Name: ", EVENT_NONE, 5, 170, 200, 18, rangename.val, 50, "Name for Range Object") + drawWidget(widmenu.val) + PushButton("Back", EVENT_BACK, 5, 25, 50, 18) + PushButton("Finish", EVENT_NEXT, 55, 25, 50, 18) + return + + + +def event(evt, val): + if (evt == QKEY and not val): + Exit() + + +def bevent(evt): + global widmenu,stage,drawtype + ######### Manages GUI events + if evt==EVENT_EXIT: + Exit() + elif evt==EVENT_BACK: + if stage == 2: + stage = 1 + Redraw() + elif evt==EVENT_NEXT: + if stage == 1: + stage = 2 + Redraw() + elif stage == 2: + build(widmenu.val) + stage = 1 + Window.RedrawAll() + + +Register(draw, event, bevent) diff --git a/release/scripts/xsi_export.py b/release/scripts/xsi_export.py new file mode 100644 index 00000000000..0f31024c799 --- /dev/null +++ b/release/scripts/xsi_export.py @@ -0,0 +1,1245 @@ +#!BPY + + +""" +Name: 'SoftImage XSI (.xsi)...' +Blender: 236 +Group: 'Export' +Tooltip: 'Export to a SoftImage XSI file' +""" + +__author__ = ("Elira") +__url__ = ["Author's site, http://www.creative-realms.net/~elira/blender.html", +"SoftImage's site, www.softimage.com", "elysiun"] +__email__ = ["scripts"] +__version__ = "2005/11/01" + + +__bpydoc__ = """\ +This script exports to the XSI format. + +Usage: + +Run this script from "File->Export" menu. + +Note:<br> +- Updates by Mal Duffin, to assist with XSI to Shockwave 3D conversion. +""" + +# $Id: xsi_export.py,v 1.4.6 2005/11/01 +# +#------------------------------------------------------------------------ +# XSI exporter for blender 2.36 or above +# +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# ***** END GPL LICENCE BLOCK ***** +# + + +# +# --------------------------------------------------------------------------- +# XSI Export V 1.4.1 by Elira (at) creative-realms (dot) net +# +# Updates by Mal Duffin, to assist with XSI to Shockwave 3D conversion +# --------------------------------------------------------------------------- +# 0.0.0 - This header and having blender ID the file. +# 0.1.0 - Output the statis xsi header elements +# 0.2.0 - create a full shell output (no content just structure) +# 0.3.0 - output used materials from the full materials list +# 0.4.0 - output the object model minor data +# 0.5.0 - output the object shape data, storing a uv table +# 0.6.0 - output the triangle lists (uv references stored uv table) +# 0.7.0 - convert output to genuine file writes. +# 1.0.0 - Admit this script exists and wait for flames +# 1.1.0 - Correctly export mesh shapes +# 1.2.0 - Mesh positioning corrected, added back normals +# 1.3.0 - conditionally output uv co-ordinates +# 1.4.0 - export vertex paint colours. +# --------------------------------------------------------------------------- +# 1.4.1 - added basic normal export code, +# to get XSI to Shockwave 3D converter working ( Mal Duffin ) +# 1.4.2 - invalid mesh checking +# better normal exporting +# general code clean up +# 1.4.3 - basic light exporting +# fix for ambient light being ignored by Shockwave 3D converter +# 1.4.4 - basic camera exporting +# 1.4.5 - exports normals correctly +# 1.4.6 - exports multiple materials per object +# --------------------------------------------------------------------------- +# TO DO +# - Support texturing +# - for both methods of texturing ( render method, and Game Engine method ) +# --------------------------------------------------------------------------- +# add required modules + +import Blender +from Blender import sys as bsys +from Blender import Mathutils +from Blender import Lamp +from Blender import Camera +import math + + + +# --------------------------------------------------------------------------- +# globals to make things a lot lot easier +OBJ = [] # the object list +MAT = [] # the materials list +UVC = [] # uv vert co-ords +UVI = [] # uv vert index +VCC = [] # vert colour co-ords +VCI = [] # vert colour index +FD = [] # file handle +NORMALS = [] # normal list +mats = [] +EXPORT_DIR = '' +WORLD = Blender.World.Get() + +# --------------------------------------------------------------------------- +# get_path returns the path portion o/wf the supplied filename. +# --------------------------------------------------------------------------- +def get_path(file): + l=len(file) + r=0 + for i in range(l, 0, -1): + if r == 0: + if file[i-1] == "/" or file[i-1] == "\\": + r = i + return file[:r] + + + +# --------------------------------------------------------------------------- +# r2d - radians to degrees +# --------------------------------------------------------------------------- +def r2d(r): + return round(r*180.0/math.pi,4) + + + +# --------------------------------------------------------------------------- +# d2r - degrees to radians +# --------------------------------------------------------------------------- +def d2r(d): + return (d*math.pi)/180.0 + + + +# --------------------------------------------------------------------------- +# get_filename returns the filename +# --------------------------------------------------------------------------- +def get_filename(file): + l=len(file) + r=0 + for i in range(l, 0, -1): + if r == 0: + if file[i-1] == "/" or file[i-1] == "\\": + r = i + return file[r:] + + +# --------------------------------------------------------------------------- +# find materials returns all materials on an object. +# --------------------------------------------------------------------------- +def get_materials(obj): + + # any materials attached to the object itself + mats = obj.getMaterials(0) + + if 'Mesh' != obj.getType(): + return mats + + # now drop down to the mesh level + #mesh = Blender.NMesh.GetRaw(obj.data.name) + mesh = obj.data + + if mesh.materials: + for mat in mesh.materials: + mats.append(mat) + + # return the materials list + return mats + + + +# --------------------------------------------------------------------------- +# apply_transform converts a vertex to co-ords +# --------------------------------------------------------------------------- +def apply_transform(vert, matrix): + vc = Mathutils.CopyVec(vert) + vc.resize4D() + return Mathutils.VecMultMat(vc, matrix) + + + +# --------------------------------------------------------------------------- +# do_header writes out the header data +# --------------------------------------------------------------------------- +def do_header(): + + global FD + + # this says which xsi version + FD.write("xsi 0300txt 0032\n\n") + + # static fileinfo block + FD.write("SI_FileInfo {\n") + FD.write(" \"Blender Scene\",\n") + FD.write(" \"Blender User\",\n") + FD.write(" \"Now\",\n") + FD.write(" \"xsi_export Blender Scene Exporter\",\n") + FD.write("}\n\n") + + # static scene block + FD.write("SI_Scene no_name {\n") + FD.write(" \"FRAMES\",\n") + FD.write(" 0.000000,\n") + FD.write(" 100.000000,\n") + FD.write(" 30.000000,\n") + FD.write("}\n\n") + + # static co-ordinate system block + FD.write("SI_CoordinateSystem coord {\n") + FD.write(" 1,\n") + FD.write(" 0,\n") + FD.write(" 1,\n") + FD.write(" 0,\n") + FD.write(" 5,\n") + FD.write(" 2,\n") + FD.write("}\n\n") + + # static angle block + FD.write("SI_Angle {\n") + FD.write(" 0,\n") + FD.write("}\n\n") + + # static ambience block + ambient = WORLD[0].getAmb() + + FD.write("SI_Ambience {\n") + FD.write(" %f,\n" % ambient[0]) + FD.write(" %f,\n" % ambient[1]) + FD.write(" %f,\n" % ambient[2]) + FD.write("}\n\n") + + + +# --------------------------------------------------------------------------- +# do_materiallibrary writes out the materials subsection. +# --------------------------------------------------------------------------- +def do_materiallibrary(): + + global OBJ, MAT, FD + + # set some flags first + mnum = 0 + + # run through every material, how many used? + for mat in MAT: + nmat = mat.getName() + + # first, is this material on any of the objects. + f = 0 + for obj in OBJ: + ml = get_materials(obj) + for mli in ml: + nmli = mli.getName() + if nmli == nmat: + f = 1 + mnum += 1 + break + if f == 1: + break + + bCreateDefault = 0 + # if none then exit + if not mnum: + bCreateDefault = 1 +# return + + # get to work create the materiallibrary wrapper and fill. + FD.write("SI_MaterialLibrary {\n") + FD.write(" " + str(mnum) + ",\n") + + # run through every material, write the used ones + for mat in MAT: + nmat = mat.getName() + + # find out if on any object, if so we write. + f = 0 + for obj in OBJ: + ml = get_materials(obj) + for mli in ml: + nmli = mli.getName() + if nmli == nmat: + do_material(mat) + f = 1 + break + if f == 1: + break + + if bCreateDefault == 1: + do_material ( 0 ) + + # clean up + FD.write("}\n\n") + + +def removeSpacesFromName(name): + name = name.replace ( " ", "_" ) + return name + + +# --------------------------------------------------------------------------- +# do_material writes out this material. +# --------------------------------------------------------------------------- +def do_material(mat): + + global FD + + if mat == 0: + name = "__default" + cr = 1.0 + cg = 1.0 + cb = 1.0 + ca = 1.0 + sp = 0.0 + sr = 0.0 + sg = 0.0 + sb = 0.0 + em = 0.0 + am = 1.0 + sm = 0 + else: + + + # get the name first + name = mat.getName() + + # face colour r, g, b, a + # power (spec decay) fl + # spec colour r, g, b + # emmisive colourm r, g, b + # shading model int constant, lambert, phong, blinn, shadow, vertex + # ambient colour r, g, b + + # get and print the base material block + cr, cg, cb = mat.getRGBCol() + ca = mat.getAlpha() + + sp = 0.0 + sr, sg, sb = mat.getSpecCol() + em = mat.getEmit() + am = mat.getAmb() + + # how do we render this material? start with constant (0) + sm = 0 + fl = mat.getMode() + if fl & Blender.Material.Modes['VCOL_PAINT']: + sm = 5 + + + FD.write(" SI_Material " + removeSpacesFromName(name) + " {\n") + FD.write(" %f,\n" % cr) + FD.write(" %f,\n" % cg) + FD.write(" %f,\n" % cb) + FD.write(" %f,\n" % ca) + FD.write(" %f,\n" % sp) + FD.write(" %f,\n" % sr) + FD.write(" %f,\n" % sg) + FD.write(" %f,\n" % sb) + FD.write(" %f,\n" % em) + FD.write(" %f,\n" % em) + FD.write(" %f,\n" % em) + FD.write(" %d,\n" % sm) + #FD.write(" %f,\n" % am) + #FD.write(" %f,\n" % am) + #FD.write(" %f,\n" % am) + FD.write(" %f,\n" % cr) + FD.write(" %f,\n" % cg) + FD.write(" %f,\n" % cb) + + if mat != 0: + # if this material has a texture, then add here + mtex = mat.getTextures() + for mt in mtex: + if mt: + do_texture(mt) + + FD.write(" }\n") + + + +# --------------------------------------------------------------------------- +# do_texture writes out this texture if usable. +# --------------------------------------------------------------------------- +def do_texture(mtex): + global FD + + + # get our texture + tex = mtex.tex + tn = tex.getName() + + + # what type of texture, we are limitd + if tex.type != Blender.Texture.Types.IMAGE: + return + + + FD.write(" SI_Texture2D " + tn + " {\n") + + img = tex.getImage() + iname = get_filename(img.getFilename()) + + FD.write(" \"" + iname + "\",\n") + + # mapping type ? uv map wrapped is 4, how to detect? + # start with a simple xy mapping ie 0 + FD.write(" 4,\n") + + print img.getSize () + + # image width, and height + ix, iy = img.getSize() + FD.write(" %d,\n" % ix) + FD.write(" %d,\n" % iy) + # u crop min/max, v crop min/max + mincu, mincv, maxcu, maxcv = tex.crop + FD.write(" %d,\n" % ( mincu * ix ) ) + FD.write(" %d,\n" % ( maxcu * ix - 1 ) ) + FD.write(" %d,\n" % ( mincv * iy ) ) + FD.write(" %d,\n" % ( maxcv * iy - 1) ) + # uv swap + uvs =0 + if (tex.flags & Blender.Texture.Flags.FLIPBLEND): + uvs = 1 + FD.write(" %d,\n" % uvs ) + # u/v repeat + iru = img.getXRep() + FD.write(" %d,\n" % iru ) + irv = img.getYRep() + FD.write(" %d,\n" % irv ) + # u/v alt - 0, 0 + FD.write(" 0,\n" ) + FD.write(" 0,\n" ) + # u/v scale - 1,1 + FD.write(" 1.000000,\n" ) + FD.write(" 1.000000,\n" ) + # u/v offset - 0,0 + FD.write(" 0.000000,\n" ) + FD.write(" 0.000000,\n" ) + # proj mat 4x4 1 0 0 0, 0 1 0 0, 0 0 1 0, 0 0 0 1 is default + FD.write(" 1.000000,\n" ) + FD.write(" 0.000000,\n" ) + FD.write(" 0.000000,\n" ) + FD.write(" 0.000000,\n" ) + + FD.write(" 0.000000,\n" ) + FD.write(" 1.000000,\n" ) + FD.write(" 0.000000,\n" ) + FD.write(" 0.000000,\n" ) + + FD.write(" 0.000000,\n" ) + FD.write(" 0.000000,\n" ) + FD.write(" 1.000000,\n" ) + FD.write(" 0.000000,\n" ) + + FD.write(" 0.000000,\n" ) + FD.write(" 0.000000,\n" ) + FD.write(" 0.000000,\n" ) + FD.write(" 1.000000,\n" ) + + # blending type - 3 + FD.write(" 3,\n" ) + # blending - 1 + FD.write(" 1.000000,\n" ) + # ambient - 0 + FD.write(" 0.000000,\n" ) + # diffuse - 1 + FD.write(" 1.000000,\n" ) + # speculara - 0 + FD.write(" 0.000000,\n" ) + # transparent - 0 + FD.write(" 0.000000,\n" ) + # reflective - 0 + FD.write(" 0.000000,\n" ) + # roughness - 0 + FD.write(" 0.000000,\n" ) + + # close off this texture + FD.write(" }\n") + + + +# --------------------------------------------------------------------------- +# do_model_transform dumps out the transform data +# --------------------------------------------------------------------------- +def do_model_transform(obj): + + global FD + + # now output + FD.write(" SI_Transform SRT-" + removeSpacesFromName( obj.getName() ) + " {\n" ) + + + + # write out the object size? (scaling) + FD.write(" %f,\n" % obj.SizeX ) + FD.write(" %f,\n" % obj.SizeY ) + FD.write(" %f,\n" % obj.SizeZ ) + + # write out the object rotation + FD.write(" %f,\n" % r2d(obj.RotX) ) + FD.write(" %f,\n" % r2d(obj.RotY) ) + FD.write(" %f,\n" % r2d(obj.RotZ) ) + + # this is the position of the object's axis + FD.write(" %f,\n" % obj.LocX ) + FD.write(" %f,\n" % obj.LocY ) + FD.write(" %f,\n" % obj.LocZ ) + FD.write(" }\n\n") + + + +# --------------------------------------------------------------------------- +# do_model_visibility marks if the model is visible or not??? +# --------------------------------------------------------------------------- +def do_model_visibility(obj): + + global FD + + # for now this is a static block + FD.write(" SI_Visibility {\n" ) + FD.write(" 1,\n" ) + FD.write(" }\n\n" ) + + + +# --------------------------------------------------------------------------- +# do_model_material sets the global material for the model +# --------------------------------------------------------------------------- +def do_model_material(obj): + + global FD + + # do we have one? + ml = get_materials(obj) + + + n = 0 + for mli in ml: + if mli: + n+=1 + if n == 1: + mat=mli + + + # if no materials just go back + if n == 0: + return + + # for now we grab the first material on the list. + + for mat in ml: + FD.write(" SI_GlobalMaterial {\n" ) + FD.write(" \"" + removeSpacesFromName(mat.getName()) + "\",\n" ) + FD.write(" \"NODE\",\n" ) + FD.write(" }\n\n" ) + + + +def meshHasUV ( mesh ): + if mesh.hasFaceUV(): + return TRUE +# materials = mesh.materials +# if len(materials) > 0: + + return FALSE + +# --------------------------------------------------------------------------- +# do_collect_uv, makes an easy to use list out of the uv data +# todo, remove duplicates and compress the list size, xsi supports this. +# --------------------------------------------------------------------------- +def do_collect_uv(mesh): + + global UVC, UVI + + # reset the uv details first. + UVI = [] + UVC = [] + + #print "Textures..." + #mtex = mat.getTextures() + #for mt in mtex: + # print mt + + + # if no uv data then return + if not mesh.hasFaceUV(): + return + + # run through all the faces + j = 0 + for f in mesh.faces: + for i in range(len(f)): + UVI.append(j) + UVC.append(f.uv[i]) + j+=1 + UVI.append(-1) + + + +# --------------------------------------------------------------------------- +# do_collect_colour, makes an easy to use list out of the colour data +# todo, remove duplicates and compress the list size, xsi supports this. +# --------------------------------------------------------------------------- +def do_collect_colour(mesh): + + global VCC, VCI + + # reset the uv details first. + VCC = [] + VCI = [] + + # if no uv data then return + if not mesh.hasVertexColours(): + return + + # run through all the faces + j = 0 + for f in mesh.faces: + for i in range(len(f)): + VCI.append(j) + VCC.append(f.col[i]) + j+=1 + VCI.append(-1) + + + +# --------------------------------------------------------------------------- +# do_mesh_shape outputs the shape data +# --------------------------------------------------------------------------- +def do_mesh_shape(obj): + + global UVC, UVI, VCC, VCI, FD, NORMALS + + # Grab the mesh itself + mesh = Blender.NMesh.GetRaw(obj.data.name) + + # get the world matrix + matrix = obj.getMatrix('worldspace') + + # we need to decide about vertex and uv details first. + do_collect_uv(mesh) + do_collect_colour(mesh) + + # output the shell + elements=2 + if len(UVC): + elements+=1 + if len(VCC): + elements+=1 + FD.write(" SI_Shape SHP-" + removeSpacesFromName ( obj.getName() ) + "-ORG {\n" ) + FD.write(" %d,\n" % elements ) + FD.write(" \"ORDERED\",\n\n" ) + + # vertices first + FD.write(" %d,\n" % len(mesh.verts) ) + FD.write(" \"POSITION\",\n" ) + for v in mesh.verts: + FD.write(" %f,%f,%f,\n" % (v.co[0], v.co[1], v.co[2]) ) + FD.write("\n") + + + print " MESH NAME = " + mesh.name + + NORMALS = [] + for f in mesh.faces: + NORMALS.append ( f.no ) + for v in mesh.verts: + aTemp = [v.no[0], v.no[1], v.no[2]] + NORMALS.append ( aTemp ) + + + FD.write(" %d,\n" % len(NORMALS) ) + FD.write(" \"NORMAL\",\n" ) + + for n in NORMALS: + FD.write(" %f,%f,%f,\n" % ( n[0], n[1], n[2] ) ) + + # if vertex colour data then process + if mesh.hasVertexColours(): + + # put out the co-ord header + FD.write(" %d,\n" % len(VCC) ) + FD.write(" \"COLOR\",\n" ) + + # now output them + for vc in VCC: + FD.write(" %f,%f,%f,%f,\n" % (vc.r/255.0, vc.g/255.0, vc.b/255.0, vc.a/255.0) ) + + + + # if uv data then process + if mesh.hasFaceUV(): + # put out the co-ord header + FD.write(" %d,\n" % len(UVC) ) + FD.write(" \"TEX_COORD_UV\",\n" ) + + # now output them + for uv in UVC: + FD.write(" %f,%f\n" % (uv[0], uv[1]) ) + + # close off + FD.write(" }\n" ) + + + +# --------------------------------------------------------------------------- +# do_mesh_faces outputs the faces data +# --------------------------------------------------------------------------- +def do_mesh_faces(obj): + + global FD, UVI, VCI, mats + + # do we have a texture? + ml = get_materials(obj) + n = 0 + for mli in ml: + if mli: + n+=1 + if n == 1: + mat=mli + + # Grab the mesh itself + # mesh = Blender.NMesh.GetRaw(obj.data.name) + + # mesh = Blender.NMesh.GetRawFromObject(obj.name) + + mesh = obj.data + + + + tris = [] + normalX = [] + mats = [] + for f in mesh.faces: + tris.extend ( triangulate_face(f) ) + aVal = triangulate_normals(mesh,f) + + for v in aVal: + normalX.append ( v ) + + + triangles = len(tris) + + if n == 0: + FD.write(" SI_TriangleList " + removeSpacesFromName(obj.getName()) + " {\n") + FD.write(" %d,\n" % triangles) + + ostring=" \"NORMAL" + if len(VCI): + ostring += "|COLOR" + if len(UVC): + ostring += "|TEX_COORD_UV" + ostring += "\",\n" + FD.write(ostring) + + FD.write(" \"\",\n\n") + + for t in tris: + FD.write(" %d,%d,%d,\n" % (t[0], t[2], t[1])) + + FD.write("\n") + + for n in normalX: + FD.write(" %d,%d,%d,\n" % ( n[0], n[1], n[2] ) ) + + # finally close this triangle list off + FD.write(" }\n\n") + + + + print "total materials" + print ml + + for mIndex in range (0,len(ml)): + mat = ml[mIndex] + print "checking materials" + print mat + + aTriCount = 0 + for tIndex in range ( 0, len ( tris ) ): + aMat = mats[tIndex] + if aMat == mIndex: + aTriCount = aTriCount + 1 + + # + # output the shell + FD.write(" SI_TriangleList " + removeSpacesFromName(obj.getName()) + " {\n") + # FD.write(" %d,\n" % triangles) + FD.write(" %d,\n" % aTriCount) + + ostring=" \"NORMAL" + if len(VCI): + ostring += "|COLOR" + if len(UVC): + ostring += "|TEX_COORD_UV" + ostring += "\",\n" + FD.write(ostring) + + + FD.write(" \"" + removeSpacesFromName ( mat.getName() ) + "\",\n\n") + +# FD.write(" \"\",\n\n") + + + for tIndex in range ( 0, len ( tris ) ): + aMat = mats[tIndex] + if mIndex == aMat: + t = tris[tIndex] + FD.write(" %d,%d,%d,\n" % (t[0], t[2], t[1])) + + FD.write("\n") + + + +# for n in normalX: + for tIndex in range ( 0, len ( tris ) ): + aMat = mats[tIndex] + if mIndex == aMat: + n = normalX[tIndex] + FD.write(" %d,%d,%d,\n" % ( n[0], n[1], n[2] ) ) + + + + # if we have it, put out the colour vertex list + # ostring = " " + # for i in range(len(VCI)): + # if a -1 its end of line, write. + # if VCI[i] == -1: + # ostring = ostring + "\n" + # FD.write(ostring) + # ostring=" " + # else: + # ostring = ostring + "%d," % VCI[i] + + # The final set is to work out the uv list, its one set per face + # ostring = " " + # for i in range(len(UVI)): + # # if a -1 its end of line, write. + # if UVI[i] == -1: + # ostring = ostring + "\n" + # FD.write(ostring) + # ostring=" " + # else: + # ostring = ostring + "%d," % UVI[i] + + # finally close this triangle list off + FD.write(" }\n\n") + + +def getNormalInfo(mesh, faceInfo): + global NORMALS + aNL = [] + for fi in faceInfo: + aN = [] + + aFace = mesh.faces[fi[0]] + + print aFace + + if (aFace.smooth): + aN.append ( NORMALS.index ( aFace.v.no[0] ) ) + aN.append ( NORMALS.index ( aFace.v.no[1] ) ) + aN.append ( NORMALS.index ( aFace.v.no[2] ) ) + else: + aN.append ( NORMALS.index ( aFace.no ) ) + aN.append ( NORMALS.index ( aFace.no ) ) + aN.append ( NORMALS.index ( aFace.no ) ) + +# aN.append ( NORMALS.index ( mesh.faces[fi[0]].no ) ) +# aN.append ( NORMALS.index ( mesh.faces[fi[0]].no ) ) +# aN.append ( NORMALS.index ( mesh.faces[fi[0]].no ) ) + + aNL.append ( aN ) + return aNL + + + +# copy of code to triangulate mesh +################################## +def triangulate_face(f): + if len(f.v) <= 3: + #newFaces = [ [f.v[0].index, f.v[1].index, f.v[2].index] ] + newFaces = [ [f.v[0].index, f.v[2].index, f.v[1].index] ] + mats.append ( f.materialIndex ) + else: + #newFaces = [ [f.v[0].index, f.v[1].index, f.v[2].index] ] + #newFaces.append ( [f.v[3].index, f.v[0].index, f.v[2].index] ) + newFaces = [ [f.v[0].index, f.v[2].index, f.v[1].index] ] + newFaces.append ( [f.v[3].index, f.v[2].index, f.v[0].index] ) + mats.append ( f.materialIndex ) + mats.append ( f.materialIndex ) + + return newFaces + +# copy of code to triangulate mesh +################################## +def triangulate_normals(mesh, f): + + if len(f.v) <= 3: + if f.smooth: + n1 = get_normal_index ( mesh, [f.v[0].no[0], f.v[0].no[1], f.v[0].no[2]] ) + n2 = get_normal_index ( mesh, [f.v[1].no[0], f.v[1].no[1], f.v[1].no[2]] ) + n3 = get_normal_index ( mesh, [f.v[2].no[0], f.v[2].no[1], f.v[2].no[2]] ) + newNormals = [[ n1, n2, n3 ]] + else: + n1 = get_normal_index ( mesh, [f.no[0], f.no[1], f.no[2]] ) + newNormals = [[ n1, n1, n1 ]] + else: + if f.smooth: + n1 = get_normal_index ( mesh, [f.v[0].no[0], f.v[0].no[1], f.v[0].no[2]] ) + n2 = get_normal_index ( mesh, [f.v[1].no[0], f.v[1].no[1], f.v[1].no[2]] ) + n3 = get_normal_index ( mesh, [f.v[2].no[0], f.v[2].no[1], f.v[2].no[2]] ) + n4 = get_normal_index ( mesh, [f.v[3].no[0], f.v[3].no[1], f.v[3].no[2]] ) + newNormals = [ [ n1, n2, n3 ] ] + newNormals.append ( [ n4, n1, n3 ] ) + +# newNormals = [[ n1, n3, n2 ]] +# newNormals.append ( [ n4, n3, n1 ] ) + else: + n1 = get_normal_index ( mesh, [f.no[0], f.no[1], f.no[2]] ) + newNormals = [[ n1, n1, n1 ]] + newNormals.append ( [ n1, n1, n1 ] ) + + return newNormals + + + +################################## +def get_normal_index(mesh,normal): + global NORMALS + + indx=NORMALS.index(normal) + return indx + + +# --------------------------------------------------------------------------- +# do_model_mesh outputs the shape/triangelist wrapper block +# --------------------------------------------------------------------------- +def do_model_mesh(obj): + + global FD + + # output the shell + FD.write(" SI_Mesh MSH-" + removeSpacesFromName(obj.getName()) + " {\n") + + # todo, add calc normals and calc uv here + # these can be used in both the following sections. + + # next the shape + do_mesh_shape(obj) + + # finally the trangle list + do_mesh_faces(obj) + + # finally close this mesh off + FD.write(" }\n\n") + + + +# --------------------------------------------------------------------------- +# do_model actually outputs a mesh model +# --------------------------------------------------------------------------- +def do_model(obj): + + global FD + + # we only want meshes for now. + if 'Mesh' != obj.getType(): + return + + # check if the mesh is valid + if validMesh(obj) <> 0: + print "INVALID MESH " + obj.getName () + return + + + print "Exporting model " + obj.getName () + + # start model + FD.write(" SI_Model MDL-" + removeSpacesFromName(obj.getName()) + " {\n") + + # do transform + do_model_transform(obj) + + # do visibility + do_model_visibility(obj) + + # do global material + do_model_material(obj) + + # do the mesh + do_model_mesh(obj) + + # close this model + FD.write(" }\n") + +# +# check for invalid mesh ( faces that have < 3 vertices ) +# + +def validMesh (obj): + mesh = Blender.NMesh.GetRaw(obj.data.name) + for f in mesh.faces: + if len(f.v) < 3: + print "MESH HAS FACES WITH < 3 VERTICES" + return 1 + if len (mesh.faces) == 0: + print "MESH HAS NO FACES" + return 1 + + return 0 + +# --------------------------------------------------------------------------- +# do_models is the process which allows us to write out a bunch of models +# --------------------------------------------------------------------------- +def do_models(): + + global OBJ, MAT, FD + + #create the full scene wrapper object + FD.write("SI_Model MDL-SceneRoot {\n") + FD.write(" SI_Transform SRT-SceneRoot {\n" ) + FD.write(" 1.000000,\n") + FD.write(" 1.000000,\n") + FD.write(" 1.000000,\n") + FD.write(" -90.000000,\n") + FD.write(" 0.000000,\n") + FD.write(" 0.000000,\n") + FD.write(" 0.000000,\n") + FD.write(" 0.000000,\n") + FD.write(" 0.000000,\n") + FD.write(" }\n\n") + + # now process the actual selected meshes themselves + for obj in OBJ: + do_model(obj) + + for obj in OBJ: + do_light(obj) + + for obj in OBJ: + do_camera(obj) + + do_light_ambient () + + # finally close off the model list + FD.write("}\n") + + +# --------------------------------------------------------------------------- +# do_light actually outputs a light model +# --------------------------------------------------------------------------- +def do_light(obj): + + global FD + + # we only want lights for now. + if 'Lamp' != obj.getType(): + return + + print "Exporting light " + obj.getName () + + aLampType = 1 + + lmpName=Lamp.Get(obj.data.getName()) + lmpType=lmpName.getType() + + if lmpType == Lamp.Types.Lamp: + aLampType = 0 + elif lmpType == Lamp.Types.Spot: + aLampType = 0 + elif lmpType == Lamp.Types.Sun: + aLampType = 1 + else: + aLampType = 0 + + # start model + FD.write(" SI_Light " + removeSpacesFromName(obj.getName()) + " {\n") + + # do type + FD.write(" %d,\n" % aLampType) + + lampName=Lamp.Get(obj.data.getName()) + colour = lampName.col + + # do color + FD.write(" %f,\n" % colour[0] ) + FD.write(" %f,\n" % colour[1] ) + FD.write(" %f,\n" % colour[2] ) + + # do position + + FD.write(" %f,\n" % obj.LocX ) + FD.write(" %f,\n" % obj.LocY ) + FD.write(" %f,\n" % obj.LocZ ) + + + # close this model + FD.write(" }\n") + + +# --------------------------------------------------------------------------- +# do_light actually outputs a light model +# --------------------------------------------------------------------------- +def do_camera(obj): + + global FD + + # we only want cameras for now. + if 'Camera' != obj.getType(): + return + + print "Exporting camera " + obj.getName () + + + + # start model + FD.write(" SI_Camera " + removeSpacesFromName(obj.getName()) + " {\n") + + + cameraName=Camera.Get(obj.data.getName()) + + # colour = cameraName.col + + # do position + + FD.write(" %f,\n" % obj.LocX ) + FD.write(" %f,\n" % obj.LocY ) + FD.write(" %f,\n" % obj.LocZ ) + + # looking at + + FD.write(" %f,\n" % 0.0 ) + FD.write(" %f,\n" % 0.0 ) + FD.write(" %f,\n" % 0.0 ) + + # roll + FD.write(" %f,\n" % 0.0 ) + + aLens = cameraName.getLens() + + # field of view + FD.write(" %f,\n" % aLens ) + + # near plane + FD.write(" %f,\n" % 1.0 ) + + # far plane + FD.write(" %f,\n" % 10000000.0 ) + + + # close this model + FD.write(" }\n") + + + +# --------------------------------------------------------------------------- +# write out the ambient light ( for Shockwave 3D converter ) +# --------------------------------------------------------------------------- + +def do_light_ambient(): + ambient = WORLD[0].getAmb() + if ambient == [0.0,0.0,0.0]: + ambient = [0.5,0.5,0.5] + + FD.write(" SI_Light ambient_sw3d {\n") + + FD.write(" 9,\n") + FD.write(" %f,\n" % ambient[0]) + FD.write(" %f,\n" % ambient[1]) + FD.write(" %f,\n" % ambient[2]) + FD.write(" 0.00000000,\n") + FD.write(" 0.00000000,\n") + FD.write(" 0.00000000,\n") + + FD.write(" }\n") + + + +# --------------------------------------------------------------------------- +# export_xsi is the wrapper function to process the loading of an xsi model. +# --------------------------------------------------------------------------- +def export_xsi(filename): + + global OBJ, MAT, FD, EXPORT_DIR + + # safety check + if filename.find('.xsi', -4) <= 0: + print "XSI not found" + filename += '.xsi' + + + export_dir = bsys.dirname(filename) + if export_dir != EXPORT_DIR: + EXPORT_DIR = export_dir + + # open our output + FD = open(filename, 'w') + + # get the selected objects, otherwise get them all + #OBJ = Blender.Object.GetSelected() + #if not OBJ: + + OBJ = Blender.Object.Get() + + # we need some objects, if none specified stop + if not OBJ: + return + + # if any exist, grab the materials + MAT = Blender.Material.Get() + + # output the header data + do_header() + + # output the materials used by the selected objects. + do_materiallibrary() + + # we punch out the models, that is, the meshes themselves + do_models() + + + # finally close our file + FD.close() + + + +# --------------------------------------------------------------------------- +# Lets trigger it off now +# Blender.Window.FileSelector(export_xsi, 'Export SoftImage XSI') + +fname = bsys.makename(ext=".xsi") +if EXPORT_DIR <> '': + fname = bsys.join(EXPORT_DIR, bsys.basename(fname)) + +Blender.Window.FileSelector(export_xsi, "Export SoftImage XSI", fname) |