Blender 2.57, which is the Release Candidate for the 2.5 branch, was posted a few days ago… and broke compatibility with most scripts.
I've updated my 2.56 script to work with 2.57. I haven't had time to add the armature parsing yet, but I did add an option to rotate the object to face front.
For some reason, this isn't working correctly as an addon right now. If you open the script in Blender and run it, it will add a menu item. When I've figured out why it doesn't work as an addon, I'll post the fixed version.
Code: Select all
bl_addon_info = {
"name": "Import Dragon Age 2 Mesh files (.msh)",
"author": "FigureSculptor",
"version": (2, 1),
"blender": (2, 5, 7),
"api": 35622,
"location": "File > Import ",
"description": "Import Dragon Age 2 Mes (.msh)",
"warning": "",
"wiki_url": "",
"tracker_url": "",
"category": "Import-Export"}
"""
Version': '1.0'
This is a straight port of the 3DS Max Script from this forum
posting:
http://forum.xentax.com/viewtopic.php?f=16&t=6119&hilit=dragon+age+2
This version is based on the script posted with this timestamp
Posted: Mon Mar 14, 2011 3:22 am
All credit to chrrox for figuring the format out.
"""
import bpy
import mathutils
import os
import sys
import string
import math
import re
from string import *
from struct import *
from math import *
import mathutils
from bpy.props import *
DEBUGLOG = False
LONGSIZE = 4
FLOATSIZE = 4
HALFFLOATSIZE = 2
SHORTSIZE = 2
def unpack_list(list_of_tuples):
l = []
for t in list_of_tuples:
l.extend(t)
return l
def halfToFloatPrivate(h):
s = int((h >> 15) & 0x00000001) # sign
e = int((h >> 10) & 0x0000001f) # exponent
f = int(h & 0x000003ff) # fraction
if e == 0:
if f == 0:
return int(s << 31)
else:
while not (f & 0x00000400):
f <<= 1
e -= 1
e += 1
f &= ~0x00000400
elif e == 31:
if f == 0:
return int((s << 31) | 0x7f800000)
else:
return int((s << 31) | 0x7f800000 | (f << 13))
e = e + (127 -15)
f = f << 13
return int((s << 31) | (e << 23) | f)
def halfToFloat(h):
result = halfToFloatPrivate(h)
str = pack('I',result)
f = unpack('f', str)
return f[0]
def mshImport(infile, props):
global DEBUGLOG
print ("--------------------------------------------------")
print ("---------SCRIPT EXECUTING PYTHON IMPORTER---------")
print ("--------------------------------------------------")
print ("Importing file: ", infile)
mshfile = open(infile,'rb')
if (DEBUGLOG):
logpath = infile.replace(".msh", ".txt")
print("logpath:",logpath)
logf = open(logpath,'w')
def printlog(strdata):
if (DEBUGLOG):
logf.write(strdata + "\n")
basename = os.path.basename(infile)
printlog("basename:" + basename)
mshfile.seek(0x1A0)
baseoff = 0x1A0
namebase, garbage, unkbase, vertbase, facebase, garbage1, garbage2, meshcount = unpack('<8l', mshfile.read(8*LONGSIZE))
printlog("baseoff: " + str(baseoff))
printlog("unkbase: " + str(unkbase))
printlog("vertbase: " + str(vertbase))
printlog("facebase: " + str(facebase))
printlog("meshcount: " + str(meshcount))
vertSizeArray = []
vertCountArray = []
faceCountArray = []
vertPosArray = []
facePosArray = []
for i in range(meshcount):
printlog("assessing mesh " + str(i))
float01, float02, float03, float04, float11, float12, float13, float14, float21, float22, float23, float34 = unpack('<12f', mshfile.read(12*FLOATSIZE))
unk01, vertSize, vertCount, faceCount, vertPos, facePos, unk2, long01, long02, long03, long04, vertCount2 = unpack('<12l', mshfile.read(12*LONGSIZE))
printlog("\tvertsize: " + str(vertSize))
printlog("\tvertCount: " + str(vertCount))
printlog("\tfaceCount: " + str(faceCount))
printlog("\tvertPos: " + str(vertPos))
printlog("\tfacePos: " + str(facePos))
vertSizeArray.append(vertSize)
vertCountArray.append(vertCount)
faceCountArray.append(faceCount)
vertPosArray.append(vertPos)
facePosArray.append(facePos)
faceStart = baseoff + facebase + 4
vertStart = baseoff + vertbase + 4
nameStart = baseoff + namebase
unkStart = baseoff + unkbase
if (DEBUGLOG):
printlog("faceStart: " + str(faceStart))
printlog("vertStart: " + str(vertStart))
printlog("nameStart: " + str(nameStart))
printlog("unkStart: " + str(unkStart))
for a in range(meshcount):
me_ob = bpy.data.meshes.new(basename + str(a))
printlog(("New Mesh = " + me_ob.name))
vertArray = []
uvArray = []
normalArray = []
faceArray = []
printlog("===Offset = " + str(vertStart + vertPosArray[a]))
printlog("===vertStrt = " + str(vertStart))
printlog("===vertPos[a] = " + str(vertPosArray[a]))
mshfile.seek(vertStart + vertPosArray[a])
for b in range(vertCountArray[a]):
data = mshfile.read(3*FLOATSIZE)
vx, vy, vz = unpack('<3f', data)
bytesOfUnknown = (vertSizeArray[a] - 16)
unknown = mshfile.read(bytesOfUnknown)
tu_h = unpack('H', mshfile.read(HALFFLOATSIZE))
tv_h = unpack('H', mshfile.read(HALFFLOATSIZE))
tu = halfToFloat(tu_h[0])
tv = halfToFloat(tv_h[0])
vertArray.extend([(vx, vy, vz)])
uvArray.extend([(tu, tv)])
printlog("facePosArray: " + str(facePosArray[a]))
mshfile.seek(faceStart + facePosArray[a] * 2)
faceLen = faceCountArray[a]
printlog("faceCountArray length: " + str(faceLen))
if (faceLen > 0):
faceLen = faceLen / 3
else:
faceLen = 0
printlog("facelen: " + str(faceLen))
faceUVArray = []
for b in range(int(faceLen)):
f1, f2, f3 = unpack('<3h', mshfile.read(3*SHORTSIZE))
if (f1 < len(vertArray) and f2 < len(vertArray) and f3 < len(vertArray)):
faceArray.extend([f1, f2, f3, 0])
faceUVArray.extend([(uvArray[f1], uvArray[f2], uvArray[f3])])
#printlog("Adding array:" + str(f1) + ", " + str(f2) + ", " + str(f3))
printlog("vertArray length: " + str(len(vertArray)))
printlog("faceArray length: " + str(len(faceArray)))
me_ob.vertices.add(len(vertArray))
me_ob.faces.add(len(faceArray)//4)
me_ob.vertices.foreach_set("co", unpack_list(vertArray))
me_ob.faces.foreach_set("vertices_raw", faceArray)
me_ob.faces.foreach_set("use_smooth", [False] * len(me_ob.faces))
texture = []
texturename = basename + "_tex_" + str(a)
if (len(faceUVArray) > 0):
uvtex = me_ob.uv_textures.new() #add one uv texture
uvtex.name = "Imported UV"
for i, face in enumerate(me_ob.faces):
blender_tface= uvtex.data[i] #face
blender_tface.uv1 = faceUVArray[i][0] #uv = (0,0)
blender_tface.uv2 = faceUVArray[i][1] #uv = (0,0)
blender_tface.uv3 = faceUVArray[i][2] #uv = (0,0)
texture.append(uvtex)
materialname = "mat"
materials = []
matdata = bpy.data.materials.new(materialname)
matdata.diffuse_color=(float(0.04),float(0.08),float(0.44))#blue color
texdata = None
texIndex = len(bpy.data.textures) - 1
if (texIndex >= 0):
texdata = bpy.data.textures[len(bpy.data.textures)-1]
if (texdata != None):
texdata.name = "texturelist1"
matdata.active_texture = texdata
materials.append(matdata)
for material in materials:
#add material to the mesh list of materials
me_ob.materials.append(material)
me_ob.update()
obmesh = bpy.data.objects.new(basename,me_ob)
bpy.context.scene.objects.link(obmesh)
if props.rotate_z_180:
mat_z180 = mathutils.Matrix.Rotation(math.pi, 4, 'Z')
me_ob.transform(mat_z180)
bpy.context.scene.update()
def getInputFilename(filename, props):
checktype = filename.split('\\')[-1].split('.')[1]
print ("------------",filename)
if checktype.upper() != 'MSH':
print (" Selected file = ",filename)
raise (IOError, "The selected input file is not a *.msh file")
mshImport(filename, props)
class IMPORT_OT_dragonage_mesh(bpy.types.Operator):
'''Import from Dragon Age Mesh file (.msh)'''
bl_idname = "import_scene.dragonage_msh"
bl_description = 'Import from Dragon Age mesh file (.msh)'
bl_label = "Import Dragon Age Mesh"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
rotate_z_180 = BoolProperty(name="Rotate Z 180",
description="Rotate 180 degrees around Z so model faces front.",
default=True)
# List of operator properties, the attributes will be assigned
# to the class instance from the operator settings before calling.
filepath = StringProperty(name="File Path", description="Filepath used for importing the MSH file", maxlen= 1024, default= "", subtype='FILE_PATH')
def execute(self, context):
props = self.properties
getInputFilename(self.filepath, props)
return {'FINISHED'}
def invoke(self, context, event):
wm = context.window_manager
wm.fileselect_add(self)
return {'RUNNING_MODAL'}
def menu_func(self, context):
self.layout.operator(IMPORT_OT_dragonage_mesh.bl_idname, text="Dragon Age 2 Mesh (.msh)")
def register():
bpy.utils.register_module(__name__)
bpy.types.INFO_MT_file_import.append(menu_func)
def unregister():
bpy.utils.unregister_module(__name__)
bpy.types.INFO_MT_file_import.remove(menu_func)
if __name__ == "__main__":
register()