Absolutely no idea.
I've been trying to figure this out for the longest time now, but I think I've only narrowed it down to the way Bobcatben has it written in his code. (I'm too scared to edit and try it out, though.) From the Import script:
#9/12/2007 version 0.3: reAdded importing of normals(results in some duplicate vertices's but fixes the normal problem, still recalculating them, but this keeps it from merging vertices's with different normals)So... I haven't gotten the gist of Python scripts. Hopefully someone out there knows how to read/code them and figure out a way to import the normals without recalculating them.
ETA: Here are how the actual scripts look like, if anyone is interested:
#!BPY
"""
Name: 'Sims 2 smd file'
Blender: 244
Group: 'Import'
Tooltip: 'imports a smd file from sims 2(doubt it will work on other smd's)'
"""
__author__ = "Bobcatben"
__url__ = ["", ""]
__version__ = "0.3"
__bpydoc__ = """\
"""
#9/12/2007 version 0.3: reAdded importing of normals(results in some duplicate vertices's but fixes the normal problem, still recalculating them, but this keeps it from merging vertices's with different normals)
from Blender import *
from Blender.Mathutils import *
from math import *
def mat(x,y,z,rx,ry,rz):
rotmat = RotationMatrix(rx,4,'x') * RotationMatrix(ry,4,'y') * RotationMatrix(rz,4,'z')
transmat = TranslationMatrix(Vector(x,y,z))
mat2 = rotmat * transmat
del rotmat
del transmat
return mat2
class Bone:
id = 0
name = ""
parentid = 0
matrix = None
def __init__(this,id,name,pid):
this.id = id
this.name = name
this.parentid = pid
def add(a, tup):
if tup in a:
return a.index(tup)
else:
a.append(tup)
return len(a)-1
def import_smd(path):
editmode = Window.EditMode()
if editmode: Window.EditMode(0)
IMPORT_SKELETON = Draw.Create(1)
IMPORT_MESH = Draw.Create(1)
pup_block = [\
('Import Options'),\
('Mesh' ,IMPORT_MESH , 'Import the Mesh?'),\
('Skeleton' ,IMPORT_SKELETON , 'Import the Skeleton?')
]
if not Draw.PupBlock('Import...', pup_block):
return
import_mesh = IMPORT_MESH.val
import_skeleton = IMPORT_SKELETON.val
Window.WaitCursor(1)
name = path.split('\\')[-1].split('/')[-1]
file = open(path, 'r')
scn = Scene.GetCurrent()
Bones = []
faces = []
verts = []
Window.DrawProgressBar(0.0,'')
lines = file.readlines()
for i in range(0, len(lines)):
line = lines[i]
words = line.split()
#do the nodes
if line.startswith('nodes') and import_skeleton:
Window.DrawProgressBar(0.0,'Reading Nodes')
arm = Armature.New('Bones')
arm.envelopes = False
arm.makeEditable()
armob = scn.objects.new(arm,"Bones")
start = i + 1
for i in range(start, len(lines)):
line = lines[i]
words = line.split()
if line.startswith('end'):
break
tmpbone = Armature.Editbone()
bonename = words[1][1:-1]
Bones.append(Bone(int(words[0]),bonename,int(words[2])))
if int(words[2]) > -1:
tmpbone.parent = arm.bones[ Bones[int(words[2])].name ]
arm.bones[bonename] = tmpbone
#do the skeleton
if line.startswith('skeleton') and import_skeleton:
Window.DrawProgressBar(0.1,'Reading Skeleton')
start = i + 1
for i in range(start, len(lines)):
line = lines[i]
words = line.split()
if line.startswith('end'):
break
if len(words) <= 2:
continue
tx, ty, tz = float(words[1]), float(words[2]), float(words[3])
rx, ry, rz = degrees(float(words[4])), degrees(float(words[5])), degrees(float(words[6]))
boneindex = int(words[0])
bone = arm.bones[Bones[boneindex].name]
parentindex = Bones[boneindex].parentid
matrix = mat(tx,ty,tz,rx,ry,rz)
if(parentindex > -1):
parent = arm.bones[Bones[parentindex].name]
parentmatrix = Bones[parentindex].matrix
matrix = matrix * parentmatrix
myhead = Vector([0,0,0,1]) * matrix
mytail = Vector([0,0.1,0,1]) * matrix
myhead.resize3D()
mytail.resize3D()
bone.head = myhead
bone.tail = mytail
Bones[boneindex].matrix = matrix
else:
myhead = Vector([0,0,0,1]) * matrix
mytail = Vector([0,0.1,0,1]) * matrix
myhead.resize3D()
mytail.resize3D()
bone.head = myhead
bone.tail = mytail
Bones[boneindex].matrix = matrix
#do the mesh itself(triangles)
if line.startswith('triangles') and import_mesh:
Window.DrawProgressBar(0.2,'Reading Mesh')
start = i + 1
faceverts = []
facetex = []
for i in range(start, len(lines)):
line = lines[i]
words = line.split()
if line.startswith('end'):
break
if len(words) < 3:
continue
#get the vertex data, excluding normals as they dont like to import correctly
#so we will just have blender recalculate normals at the end, after we remove duplicate vertex's
x, y, z, tx, ty = float(words[1]), float(words[2]), float(words[3]), float(words[7]), float(words[8])
nx, ny, nz = float(words[4]), float(words[5]), float(words[6])
tmp = []
if len(words) >= 10:
for w in range(0,int(words[9])*2,2):
tmp.append([ int(words[10+w]), float(words[11+w]) ])
faceverts.append(add(verts,[x, y, z,nx,ny,nz, tmp])+1)
facetex.append([tx, ty])
#faces every 3 vertex's
if len(faceverts) == 3:
faces.append([faceverts,facetex])
facetex = []
faceverts = []
#now send them to blender.
Window.DrawProgressBar(0.4,'Writing Vertices')
mesh = Mesh.New('Imported Object')
meshob = scn.objects.new(mesh, 'Imported Object')
mesh.verts.extend([[0,0,0]])
for v in verts:
mesh.verts.extend([[v[0],v[1],v[2]]])
mesh.verts[-1:][0].no[0] = v[3]
mesh.verts[-1:][0].no[1] = v[4]
mesh.verts[-1:][0].no[2] = v[5]
Window.DrawProgressBar(0.6,'Writing Faces')
for f in faces:
mesh.faces.extend([f[0]],ignoreDups=True)
mesh.faces[len(mesh.faces)-1].image = Image.GetCurrent()
mesh.faces[len(mesh.faces)-1].uv = [Vector(f[1][0]),Vector(f[1][1]),Vector(f[1][2])]
mesh.verts.delete([0])
mesh.calcNormals()
file.close()
if import_skeleton and import_mesh:
Window.DrawProgressBar(0.9,'Creating vertex groups')
for b in Bones:
mesh.addVertGroup(b.name)
Window.DrawProgressBar(0.95,'Weighting vertices')
for vi in range(0, len(verts)):
for weight in verts[vi][6]:
mesh.assignVertsToGroup( Bones[weight[0]].name, [vi],weight[1],1)
if import_skeleton:
arm.update()
if import_skeleton and import_mesh:
armob.makeParentDeform([meshob], 0, 0)
Window.DrawProgressBar(1.0,'Done')
Window.WaitCursor(0)
Window.RedrawAll()
Window.FileSelector(import_smd, 'Import')
#!BPY
"""
Name: 'Sims 2 smd file'
Blender: 244
Group: 'Export'
Tooltip: 'Exports a smd file tailored for the sims 2'
"""
__author__ = "Bobcatben"
__url__ = ["", ""]
__version__ = "0.5"
__bpydoc__ = """\
"""
#Oct 27th 2007, V0.5: Added Normalization of bone weighting.
#Sept 22nd 2007, V0.4: Added more error handlers.
#Sept 20th 2007, Version 0.3: Added a check for vertex groups with no matching bones.
from Blender import *
from Blender.Mathutils import *
from math import *
armmat = Matrix()
class Bone:
id = 0
name = ""
parentid = 0
matrix = None
pos = Vector()
rot = Euler()
def __init__(this,id,name,pid,mat):
this.id = id
this.name = name
this.parentid = pid
this.matrix = mat * armmat
def findbone(name,bones):
for b in bones:
if b.name == name:
return b
def recursebones(bone,bones,pid):
thisbone = Bone(len(bones),bone.name,pid,bone.matrix['ARMATURESPACE'])
bones.append(thisbone)
for b in bone.children:
if b.hasChildren():
recursebones(b,bones,thisbone.id)
else:
bones.append(Bone(len(bones),b.name,thisbone.id,b.matrix['ARMATURESPACE']))
#rounds it to 6 decimal places
def cleanvalue(value):
if fabs( value ) < 1e-6:
return 0
else:
return round( value, 6 )
def error(text):
Draw.PupMenu("Error%t|"+text)
#checks if the object has a child that is a mesh
def hasmeshchild(obj):
for o in Object.Get():
if o.getType() == 'Mesh' and o.parent == obj:
return True
return False
#gets the first child object that is a mesh
def getmeshchild(obj):
for o in Object.Get():
if o.getType() == 'Mesh' and o.parent == obj:
return o
return None
def export_smd(path):
global armmat
editmode = Window.EditMode()
if editmode: Window.EditMode(0)
if not path.lower().endswith('.smd'):
path += '.smd'
name = path.split('\\')[-1].split('/')[-1]
#step 1, find a armature with a attached mesh
foundone = False
for o in Object.Get():
if o.getType() == 'Armature' and hasmeshchild(o):
#arm = Armature.Get(o.getName())
arm = o.getData()
armmat = o.getMatrix().rotationPart()
armmat.resize4x4()
meshob = getmeshchild(o)
mesh = meshob.getData(mesh=True)
foundone = True
break
#if step 1 fails, find just a mesh.
if not foundone:
foundmesh = False
for o in Object.Get():
if o.getType() == 'Mesh':
mesh = o.getData(mesh=True)
foundmesh = True
break
if not foundmesh:
error("No Mesh or Armature")
return
#step 2, if theres bones, process
if foundone:
Bones = []
for b in arm.bones.values():
if not b.hasParent():
recursebones(b,Bones,-1)
for b in Bones:
if b.parentid > -1:
inverseparent = Matrix(Bones[b.parentid].matrix)
inverseparent.invert()
mat = b.matrix * inverseparent
del inverseparent
else:
mat = b.matrix
b.pos = mat.translationPart()
b.rot = mat.toEuler()
del mat
#step 2.5, if no bones, generate a dummy bone
else:
Bones = [Bone(0,'SmdMiscBone',-1,Matrix())]
Bones[0].pos = Bones[0].matrix.translationPart()
Bones[0].rot = Bones[0].matrix.toEuler()
#step 3, write the beginning of the file
file = open(path, 'w')
file.write('version 1\n')
#step 4, write the bone hierarchy
file.write('nodes\n')
for b in Bones:
file.write("%d \"%s\" %d\n"%(b.id,b.name,b.parentid))
file.write('end\n')
#step 5, write the skeleton data
file.write('skeleton\n')
file.write('time 0\n')
for b in Bones:
file.write("%d %f %f %f %f %f %f\n"%(b.id,cleanvalue(b.pos.x),cleanvalue(b.pos.y),cleanvalue(b.pos.z),cleanvalue(radians(b.rot.x)),cleanvalue(radians(b.rot.y)),cleanvalue(radians(b.rot.z)) ))
file.write('end\n')
#step 6, write the mesh
file.write('triangles\n')
for face in mesh.faces:
file.write('map.bmp\n')
vi = 0
if len(face.verts) != 3:
if Draw.PupMenu("The mesh contains quads, The Sims 2 only supports triangles.%t|Convert to Triangles|Abort") == 1:
mesh.quadToTriangle()
else:
file.close()
return
for v in face.verts:
#write the vertex location
file.write("0 %f %f %f"%( cleanvalue(v.co[0]),cleanvalue(v.co[1]),cleanvalue(v.co[2]) ))
#write the vertex normal
file.write(" %f %f %f"%( cleanvalue(v.no[0]),cleanvalue(v.no[1]),cleanvalue(v.no[2]) ))
#write the uv coordinate
try:
file.write(" %f %f"%( face.uv[vi][0],face.uv[vi][1] ))
except ValueError:
error("Mesh has no texture")
file.close()
return
vi+= 1
inf = mesh.getVertexInfluences(v.index)
if len(inf) > 4:
error("Too many vertex groups on a single vertex, %d (4 max)"%len(inf))
file.close()
return
file.write(" %d"%len(inf))
if len(inf) > 0:
tweight = 0.0
mul = 1.0
for ww in inf:
tweight += ww[1]
if tweight != 1.0:
mul = 1.0 / tweight
for w in inf:
thisbone = findbone(w[0],Bones)
if thisbone != None:
file.write(" %d %f"%( thisbone.id, w[1]*mul ))
file.write('\n')
else:
file.write('\n')
file.write('end\n')
file.close()
if editmode: Window.EditMode(1)
Window.FileSelector(export_smd, 'Export')
ETA2: There is a button for Auto Smoothing in Blender's Mesh Panel, but that's turned off by default. (If it's a light-green color, it's off)