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:
Diffstat (limited to 'release/scripts/ms3d_import.py')
-rw-r--r--release/scripts/ms3d_import.py487
1 files changed, 487 insertions, 0 deletions
diff --git a/release/scripts/ms3d_import.py b/release/scripts/ms3d_import.py
new file mode 100644
index 00000000000..c1438cbfc97
--- /dev/null
+++ b/release/scripts/ms3d_import.py
@@ -0,0 +1,487 @@
+#!BPY
+"""
+Name: 'MilkShape3D (.ms3d)...'
+Blender: 245
+Group: 'Import'
+Tooltip: 'Import from MilkShape3D file format (.ms3d)'
+"""
+#
+# Author: Markus Ilmola
+# Email: markus.ilmola@pp.inet.fi
+#
+# 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.
+#
+
+# import needed stuff
+import os.path
+import math
+from math import *
+import struct
+import Blender
+from Blender import Mathutils
+from Blender.Mathutils import *
+
+
+# trims a string by removing ending 0 and everything after it
+def uku(s):
+ try:
+ return s[:s.index('\0')]
+ except:
+ return s
+
+
+# Converts ms3d euler angles to a rotation matrix
+def RM(a):
+ sy = sin(a[2])
+ cy = cos(a[2])
+ sp = sin(a[1])
+ cp = cos(a[1])
+ sr = sin(a[0])
+ cr = cos(a[0])
+ return Matrix([cp*cy, cp*sy, -sp], [sr*sp*cy+cr*-sy, sr*sp*sy+cr*cy, sr*cp],[cr*sp*cy+-sr*-sy, cr*sp*sy+-sr*cy, cr*cp])
+
+
+# Converts ms3d euler angles to a quaternion
+def RQ(a):
+ angle = a[2] * 0.5;
+ sy = sin(angle);
+ cy = cos(angle);
+ angle = a[1] * 0.5;
+ sp = sin(angle);
+ cp = cos(angle);
+ angle = a[0] * 0.5;
+ sr = sin(angle);
+ cr = cos(angle);
+ return Quaternion(cr*cp*cy+sr*sp*sy, sr*cp*cy-cr*sp*sy, cr*sp*cy+sr*cp*sy, cr*cp*sy-sr*sp*cy)
+
+
+# takes a texture filename and tries to load it
+def loadImage(path, filename):
+ image = None
+ try:
+ image = Blender.Image.Load(os.path.abspath(filename))
+ except IOError:
+ print "Warning: Failed to load image: " + filename + ". Trying short path instead...\n"
+ try:
+ image = Blender.Image.Load(os.path.dirname(path) + "/" + os.path.basename(filename))
+ except IOError:
+ print "Warning: Failed to load image: " + os.path.basename(filename) + "!\n"
+ return image
+
+
+# imports a ms3d file to the current scene
+def import_ms3d(path):
+ # get scene
+ scn = Blender.Scene.GetCurrent()
+ if scn == None:
+ return "No scene to import to!"
+
+ # open the file
+ try:
+ file = open(path, 'rb')
+ except IOError:
+ return "Failed to open the file!"
+
+ # get the file size
+ file.seek(0, os.SEEK_END);
+ fileSize = file.tell();
+ file.seek(0, os.SEEK_SET);
+
+ # read id to check if the file is a MilkShape3D file
+ id = file.read(10)
+ if id!="MS3D000000":
+ return "The file is not a MS3D file!"
+
+ # read version
+ version = struct.unpack("i", file.read(4))[0]
+ if version!=4:
+ return "The file has invalid version!"
+
+ # Create the mesh
+ scn.objects.selected = []
+ mesh = Blender.Mesh.New("MilkShape3D Mesh")
+ meshOb = scn.objects.new(mesh)
+
+ # read the number of vertices
+ numVertices = struct.unpack("H", file.read(2))[0]
+
+ # read vertices
+ coords = []
+ boneIds = []
+ for i in xrange(numVertices):
+ # skip flags
+ file.read(1)
+
+ # read coords
+ coords.append(struct.unpack("fff", file.read(3*4)))
+
+ # read bone ids
+ boneIds.append(struct.unpack("b", file.read(1))[0])
+
+ # skip refcount
+ file.read(1)
+
+ # add the vertices to the mesh
+ mesh.verts.extend(coords)
+
+ # read number of triangles
+ numTriangles = struct.unpack("H", file.read(2))[0]
+
+ # read triangles
+ faces = []
+ uvs = []
+ for i in xrange(numTriangles):
+ # skip flags
+ file.read(2)
+
+ # read indices (faces)
+ faces.append(struct.unpack("HHH", file.read(3*2)))
+
+ # read normals
+ normals = struct.unpack("fffffffff", file.read(3*3*4))
+
+ # read texture coordinates
+ s = struct.unpack("fff", file.read(3*4))
+ t = struct.unpack("fff", file.read(3*4))
+
+ # store texture coordinates
+ uvs.append([[s[0], 1-t[0]], [s[1], 1-t[1]], [s[2], 1-t[2]]])
+
+ if faces[-1][2] == 0: # Cant have zero at the third index
+ faces[-1] = faces[-1][1], faces[-1][2], faces[-1][0]
+ uvs[-1] = uvs[-1][1], uvs[-1][2], uvs[-1][0]
+
+ # skip smooth group
+ file.read(1)
+
+ # skip group
+ file.read(1)
+
+ # add the faces to the mesh
+ mesh.faces.extend(faces)
+
+ # set texture coordinates
+ for i in xrange(numTriangles):
+ mesh.faces[i].uv = [Vector(uvs[i][0]), Vector(uvs[i][1]), Vector(uvs[i][2])]
+
+ # read number of groups
+ numGroups = struct.unpack("H", file.read(2))[0]
+
+ # read groups
+ for i in xrange(numGroups):
+ # skip flags
+ file.read(1)
+
+ # skip name
+ file.read(32)
+
+ # read the number of triangles in the group
+ numGroupTriangles = struct.unpack("H", file.read(2))[0]
+
+ # read the group triangles
+ if numGroupTriangles > 0:
+ triangleIndices = struct.unpack(str(numGroupTriangles) + "H", file.read(2*numGroupTriangles));
+
+ # read material
+ material = struct.unpack("b", file.read(1))[0]
+ if material>=0:
+ for j in xrange(numGroupTriangles):
+ mesh.faces[triangleIndices[j]].mat = material
+
+ # read the number of materials
+ numMaterials = struct.unpack("H", file.read(2))[0]
+
+ # read materials
+ for i in xrange(numMaterials):
+ # read name
+ name = uku(file.read(32))
+
+ # create the material
+ mat = Blender.Material.New(name)
+ mesh.materials += [mat]
+
+ # read ambient color
+ ambient = struct.unpack("ffff", file.read(4*4))[0:3]
+ mat.setAmb((ambient[0]+ambient[1]+ambient[2])/3)
+
+ # read diffuse color
+ diffuse = struct.unpack("ffff", file.read(4*4))[0:3]
+ mat.setRGBCol(diffuse)
+
+ # read specular color
+ specular = struct.unpack("ffff", file.read(4*4))[0:3]
+ mat.setSpecCol(specular)
+
+ # read emissive color
+ emissive = struct.unpack("ffff", file.read(4*4))[0:3]
+ mat.setEmit((emissive[0]+emissive[1]+emissive[2])/3)
+
+ # read shininess
+ shininess = struct.unpack("f", file.read(4))[0]
+
+ # read transparency
+ transparency = struct.unpack("f", file.read(4))[0]
+ mat.setAlpha(transparency)
+ if transparency < 1:
+ mat.mode |= Blender.Material.Modes.ZTRANSP
+
+ # read mode
+ mode = struct.unpack("B", file.read(1))[0]
+
+ # read texturemap
+ texturemap = uku(file.read(128))
+ if len(texturemap)>0:
+ colorTexture = Blender.Texture.New(name + "_texture")
+ colorTexture.setType('Image')
+ colorTexture.setImage(loadImage(path, texturemap))
+ mat.setTexture(0, colorTexture, Blender.Texture.TexCo.UV, Blender.Texture.MapTo.COL)
+
+ # read alphamap
+ alphamap = uku(file.read(128))
+ if len(alphamap)>0:
+ alphaTexture = Blender.Texture.New(name + "_alpha")
+ alphaTexture.setType('Image')
+ alphaTexture.setImage(loadImage(path, alphamap))
+ mat.setTexture(1, alphaTexture, Blender.Texture.TexCo.UV, Blender.Texture.MapTo.ALPHA)
+
+ # read animation
+ fps = struct.unpack("f", file.read(4))[0]
+ time = struct.unpack("f", file.read(4))[0]
+ frames = struct.unpack("i", file.read(4))[0]
+
+ # read the number of joints
+ numJoints = struct.unpack("H", file.read(2))[0]
+
+ # create the armature
+ armature = 0
+ armOb = 0
+ if numJoints > 0:
+ armOb = Blender.Object.New('Armature', "MilkShape3D Skeleton")
+ armature = Blender.Armature.New("MilkShape3D Skeleton")
+ armature.drawType = Blender.Armature.STICK
+ armOb.link(armature)
+ scn.objects.link(armOb)
+ armOb.makeParentDeform([meshOb])
+ armature.makeEditable()
+
+ # read joints
+ joints = []
+ rotKeys = {}
+ posKeys = {}
+ for i in xrange(numJoints):
+ # skip flags
+ file.read(1)
+
+ # read name
+ name = uku(file.read(32))
+ joints.append(name)
+
+ # create the bone
+ bone = Blender.Armature.Editbone()
+ armature.bones[name] = bone
+
+ # read parent
+ parent = uku(file.read(32))
+ if len(parent)>0:
+ bone.parent = armature.bones[parent]
+
+ # read orientation
+ rot = struct.unpack("fff", file.read(3*4))
+
+ # read position
+ pos = struct.unpack("fff", file.read(3*4))
+
+ # set head
+ if bone.hasParent():
+ bone.head = Vector(pos) * bone.parent.matrix + bone.parent.head
+ tempM = RM(rot) * bone.parent.matrix
+ tempM.transpose;
+ bone.matrix = tempM
+ else:
+ bone.head = Vector(pos)
+ bone.matrix = RM(rot)
+
+ # set tail
+ bvec = bone.tail - bone.head
+ bvec.normalize()
+ bone.tail = bone.head + 0.01 * bvec
+
+ # Create vertex group for this bone
+ mesh.addVertGroup(name)
+ vgroup = []
+ for index, v in enumerate(boneIds):
+ if v==i:
+ vgroup.append(index)
+ mesh.assignVertsToGroup(name, vgroup, 1.0, 1)
+
+ # read the number of rotation keys
+ numKeyFramesRot = struct.unpack("H", file.read(2))[0]
+
+ # read the number of postions keys
+ numKeyFramesPos = struct.unpack("H", file.read(2))[0]
+
+ # read rotation keys
+ rotKeys[name] = []
+ for j in xrange(numKeyFramesRot):
+ # read time
+ time = fps * struct.unpack("f", file.read(4))[0]
+ # read data
+ rotKeys[name].append([time, struct.unpack("fff", file.read(3*4))])
+
+ # read position keys
+ posKeys[name] = []
+ for j in xrange(numKeyFramesPos):
+ # read time
+ time = fps * struct.unpack("f", file.read(4))[0]
+ # read data
+ posKeys[name].append([time, struct.unpack("fff", file.read(3*4))])
+
+ # create action and pose
+ action = 0
+ pose = 0
+ if armature!=0:
+ armature.update()
+ pose = armOb.getPose()
+ action = armOb.getAction()
+ if not action:
+ action = Blender.Armature.NLA.NewAction()
+ action.setActive(armOb)
+
+ # create animation key frames
+ for name, pbone in pose.bones.items():
+ # create position keys
+ for key in posKeys[name]:
+ pbone.loc = Vector(key[1])
+ pbone.insertKey(armOb, int(key[0]+0.5), Blender.Object.Pose.LOC, True)
+
+ # create rotation keys
+ for key in rotKeys[name]:
+ pbone.quat = RQ(key[1])
+ pbone.insertKey(armOb, int(key[0]+0.5), Blender.Object.Pose.ROT, True)
+
+ # The old format ends here. If there is more data then the file is newer version
+
+ # check to see if there are any comments
+ if file.tell()<fileSize:
+
+ # read sub version
+ subVersion = struct.unpack("i", file.read(4))[0]
+
+ # Is the sub version a supported one
+ if subVersion==1:
+
+ # Group comments
+ numComments = struct.unpack("i", file.read(4))[0]
+ for i in range(numComments):
+ file.read(4) # index
+ size = struct.unpack("i", file.read(4))[0] # comment size
+ if size>0:
+ print "Group comment: " + file.read(size)
+
+ # Material comments
+ numComments = struct.unpack("i", file.read(4))[0]
+ for i in range(numComments):
+ file.read(4) # index
+ size = struct.unpack("i", file.read(4))[0] # comment size
+ if size>0:
+ print "Material comment: " + file.read(size)
+
+ # Joint comments
+ numComments = struct.unpack("i", file.read(4))[0]
+ for i in range(numComments):
+ file.read(4) # index
+ size = struct.unpack("i", file.read(4))[0] # comment size
+ if size>0:
+ print "Joint comment: " + file.read(size)
+
+ # Model comments
+ numComments = struct.unpack("i", file.read(4))[0]
+ for i in range(numComments):
+ file.read(4) # index
+ size = struct.unpack("i", file.read(4))[0] # comment size
+ if size>0:
+ print "Model comment: " + file.read(size)
+
+ # Unknown version give a warning
+ else:
+ print "Warning: Unknown version!"
+
+
+ # check to see if there is any extra vertex data
+ if file.tell()<fileSize:
+
+ # read the subversion
+ subVersion = struct.unpack("i", file.read(4))[0]
+
+ # is the version supported
+ if subVersion==2:
+ # read the extra data for each vertex
+ for i in xrange(numVertices):
+ # bone ids
+ ids = struct.unpack("bbb", file.read(3))
+ # weights
+ weights = struct.unpack("BBB", file.read(3))
+ # extra
+ extra = struct.unpack("I", file.read(4))
+ # add extra vertices with weights to deform groups
+ if ids[0]>=0 or ids[1]>=0 or ids[2]>=0:
+ mesh.assignVertsToGroup(joints[boneIds[i]], [i], 0.01*weights[0], 1)
+ if ids[0]>=0:
+ mesh.assignVertsToGroup(joints[ids[0]], [i], 0.01*weights[1], 1)
+ if ids[1]>=0:
+ mesh.assignVertsToGroup(joints[ids[1]], [i], 0.01*weights[2], 1)
+ if ids[2]>=0:
+ mesh.assignVertsToGroup(joints[ids[2]], [i], 0.01*(100-(weights[0]+weights[1]+weights[2])), 1)
+
+ elif subVersion==1:
+ # read extra data for each vertex
+ for i in xrange(numVertices):
+ # bone ids
+ ids = struct.unpack("bbb", file.read(3))
+ # weights
+ weights = struct.unpack("BBB", file.read(3))
+ # add extra vertices with weights to deform groups
+ if ids[0]>=0 or ids[1]>=0 or ids[2]>=0:
+ mesh.assignVertsToGroup(joints[boneIds[i]], [i], 0.01*weights[0], 1)
+ if ids[0]>=0:
+ mesh.assignVertsToGroup(joints[ids[0]], [i], 0.01*weights[1], 1)
+ if ids[1]>=0:
+ mesh.assignVertsToGroup(joints[ids[1]], [i], 0.01*weights[2], 1)
+ if ids[2]>=0:
+ mesh.assignVertsToGroup(joints[ids[2]], [i], 0.01*(100-(weights[0]+weights[1]+weights[2])), 1)
+
+ # non supported subversion give a warning
+ else:
+ print "Warning: Unknown subversion!"
+
+ # rest of the extra data in the file is not imported/used
+
+ # refresh the view
+ Blender.Redraw()
+
+ # close the file
+ file.close()
+
+ # succes return empty error string
+ return ""
+
+
+# load the model
+def fileCallback(filename):
+ error = import_ms3d(filename)
+ if error!="":
+ Blender.Draw.PupMenu("An error occured during import: " + error + "|Not all data might have been imported succesfully.", 2)
+
+Blender.Window.FileSelector(fileCallback, 'Import')