Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTon Roosendaal <ton@blender.org>2006-01-11 01:10:14 +0300
committerTon Roosendaal <ton@blender.org>2006-01-11 01:10:14 +0300
commite7285229b824f959f84efe6774c506034cf0f98e (patch)
treef4c96dae29629cc5e5b1c335d93abc82647a11c5 /release
parentd594594cbe9c9eb3bc3c8a7708601e68693d324d (diff)
parent185c6bb49ce994d66fc67673b01a014161fa307d (diff)
Tuesday merger of bf-blender into orange branch.
Diffstat (limited to 'release')
-rw-r--r--release/scripts/3ds_export.py673
-rw-r--r--release/scripts/3ds_import.py662
-rw-r--r--release/scripts/blenderLipSynchro.py602
-rwxr-xr-xrelease/scripts/flt_export.py723
-rw-r--r--release/scripts/flt_filewalker.py278
-rwxr-xr-xrelease/scripts/flt_import.py1803
-rw-r--r--release/scripts/md2_export.py1016
-rw-r--r--release/scripts/md2_import.py571
-rw-r--r--release/scripts/mirror_bone_weights.py218
-rw-r--r--release/scripts/widgetwizard.py913
-rw-r--r--release/scripts/xsi_export.py1245
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)