Newer
Older
# ##### 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
bl_info = {
"name": "Import Unreal Skeleton Mesh (.psk)/Animation Set (psa)",
"author": "Darknet, flufy3d, camg188",
"location": "File > Import > Skeleton Mesh (.psk)/Animation Set (psa)",
"description": "Import Skeleleton Mesh/Animation Data",
"warning": "",
"wiki_url": "http://wiki.blender.org/index.php/Extensions:2.5/Py/"
"Scripts/Import-Export/Unreal_psk_psa",
"tracker_url": "https://projects.blender.org/tracker/index.php?"\
"func=detail&aid=21366",
"category": "Import-Export"}
"""
Version': '2.0' ported by Darknet
Unreal Tournament PSK file to Blender mesh converter V1.0
Author: D.M. Sturgeon (camg188 at the elYsium forum), ported by Darknet
Imports a *psk file to a new mesh
-No UV Texutre
-No Weight
-No Armature Bones
-No Material ID
-Export Text Log From Current Location File (Bool )
"""
import bpy
import mathutils
import math
Bastien Montagne
committed
# XXX Yuck! 'from foo import *' is really bad!
from mathutils import *
from math import *
from bpy.props import *
from string import *
from struct import *
from math import *
from bpy.props import *
bpy.types.Scene.unrealbonesize = FloatProperty(
name="Bone Length",
description="Bone Length from head to tail distance",
Bastien Montagne
committed
default=1, min=0.001, max=1000
)
#output log in to txt file
DEBUGLOG = False
scale = 1.0
bonesize = 1.0
from bpy_extras.io_utils import unpack_list, unpack_face_list
class md5_bone:
Bastien Montagne
committed
bone_index = 0
name = ""
bindpos = []
bindmat = []
origmat = []
head = []
tail = []
scale = []
Bastien Montagne
committed
parent = ""
parent_index = 0
blenderbone = None
roll = 0
def __init__(self):
Bastien Montagne
committed
self.bone_index = 0
self.name = ""
self.bindpos = [0.0] * 3
self.scale = [0.0] * 3
self.head = [0.0] * 3
self.tail = [0.0] * 3
self.bindmat = [None] * 3 # is this how you initilize a 2d-array
for i in range(3):
self.bindmat[i] = [0.0] * 3
self.origmat = [None] * 3 #is this how you initilize a 2d-array
for i in range(3):
self.origmat[i] = [0.0] * 3
self.parent = ""
self.parent_index = 0
self.blenderbone = None
def dump(self):
print ("bone index: ", self.bone_index)
print ("name: ", self.name)
print ("bind position: ", self.bindpos)
print ("bind translation matrix: ", self.bindmat)
print ("parent: ", self.parent)
print ("parent index: ", self.parent_index)
print ("blenderbone: ", self.blenderbone)
Bastien Montagne
committed
def getheadpos(pbone,bones):
Bastien Montagne
committed
pos_head = [0.0] * 3
#pos = mathutils.Vector((x,y,z)) * pbone.origmat
pos = pbone.bindmat.to_translation()
Bastien Montagne
committed
"""
tmp_bone = pbone
while tmp_bone.name != tmp_bone.parent.name:
pos = pos * tmp_bone.parent.bindmat
tmp_bone = tmp_bone.parent
"""
Bastien Montagne
committed
pos_head[0] = pos.x
pos_head[1] = pos.y
pos_head[2] = pos.z
Bastien Montagne
committed
return pos_head
Bastien Montagne
committed
def gettailpos(pbone,bones):
Bastien Montagne
committed
pos_tail = [0.0] * 3
ischildfound = False
childbone = None
childbonelist = []
for bone in bones:
if bone.parent.name == pbone.name:
ischildfound = True
childbone = bone
childbonelist.append(bone)
if ischildfound:
Bastien Montagne
committed
tmp_head = [0.0] * 3
for bone in childbonelist:
tmp_head[0] += bone.head[0]
tmp_head[1] += bone.head[1]
tmp_head[2] += bone.head[2]
tmp_head[0] /= len(childbonelist)
tmp_head[1] /= len(childbonelist)
tmp_head[2] /= len(childbonelist)
return tmp_head
else:
tmp_len = 0.0
Bastien Montagne
committed
tmp_len += (pbone.head[0] - pbone.parent.head[0]) ** 2
tmp_len += (pbone.head[1] - pbone.parent.head[1]) ** 2
tmp_len += (pbone.head[2] - pbone.parent.head[2]) ** 2
tmp_len = tmp_len ** 0.5 * 0.5
pos_tail[0] = pbone.head[0] + tmp_len * pbone.bindmat[0][0]
pos_tail[1] = pbone.head[1] + tmp_len * pbone.bindmat[1][0]
pos_tail[2] = pbone.head[2] + tmp_len * pbone.bindmat[2][0]
return pos_tail
def pskimport(infile,importmesh,importbone,bDebugLogPSK,importmultiuvtextures):
global DEBUGLOG
DEBUGLOG = bDebugLogPSK
print ("--------------------------------------------------")
print ("---------SCRIPT EXECUTING PYTHON IMPORTER---------")
print ("--------------------------------------------------")
print (" DEBUG Log:",bDebugLogPSK)
print ("Importing file: ", infile)
Bastien Montagne
committed
pskfile = open(infile,'rb')
if (DEBUGLOG):
logpath = infile.replace(".psk", ".txt")
print("logpath:",logpath)
logf = open(logpath,'w')
Bastien Montagne
committed
def printlog(strdata):
if (DEBUGLOG):
logf.write(strdata)
Bastien Montagne
committed
objName = infile.split('\\')[-1].split('.')[0]
Bastien Montagne
committed
me_ob = bpy.data.meshes.new(objName)
print("objName:",objName)
printlog(("New Mesh = " + me_ob.name + "\n"))
#read general header
Bastien Montagne
committed
indata = unpack('20s3i', pskfile.read(32))
#not using the general header at this time
#==================================================================================================
# vertex point
#==================================================================================================
#read the PNTS0000 header
Bastien Montagne
committed
indata = unpack('20s3i', pskfile.read(32))
recCount = indata[3]
Bastien Montagne
committed
printlog(("Nbr of PNTS0000 records: " + str(recCount) + "\n"))
counter = 0
verts = []
verts2 = []
while counter < recCount:
counter = counter + 1
Bastien Montagne
committed
indata = unpack('3f', pskfile.read(12))
#print(indata[0], indata[1], indata[2])
verts.extend([(indata[0], indata[1], indata[2])])
verts2.extend([(indata[0], indata[1], indata[2])])
#print([(indata[0], indata[1], indata[2])])
printlog(str(indata[0]) + "|" + str(indata[1]) + "|" + str(indata[2]) + "\n")
#Tmsh.vertices.append(NMesh.Vert(indata[0], indata[1], indata[2]))
#==================================================================================================
# UV
#==================================================================================================
#read the VTXW0000 header
Bastien Montagne
committed
indata = unpack('20s3i', pskfile.read(32))
recCount = indata[3]
Bastien Montagne
committed
printlog("Nbr of VTXW0000 records: " + str(recCount)+ "\n")
counter = 0
UVCoords = []
#UVCoords record format = [index to PNTS, U coord, v coord]
printlog("[index to PNTS, U coord, v coord]\n");
while counter < recCount:
counter = counter + 1
Bastien Montagne
committed
indata = unpack('hhffhh', pskfile.read(16))
UVCoords.append([indata[0], indata[2], indata[3]])
printlog(str(indata[0]) + "|" + str(indata[2]) + "|" + str(indata[3]) + "\n")
#print('mat index %i', indata(4))
#print([indata[0], indata[2], indata[3]])
#print([indata[1], indata[2], indata[3]])
#==================================================================================================
# Face
#==================================================================================================
#read the FACE0000 header
Bastien Montagne
committed
indata = unpack('20s3i', pskfile.read(32))
recCount = indata[3]
Bastien Montagne
committed
printlog("Nbr of FACE0000 records: " + str(recCount) + "\n")
#PSK FACE0000 fields: WdgIdx1|WdgIdx2|WdgIdx3|MatIdx|AuxMatIdx|SmthGrp
#associate MatIdx to an image, associate SmthGrp to a material
SGlist = []
counter = 0
faces = []
faceuv = []
facesmooth = []
#the psk values are: nWdgIdx1|WdgIdx2|WdgIdx3|MatIdx|AuxMatIdx|SmthGrp
printlog("nWdgIdx1|WdgIdx2|WdgIdx3|MatIdx|AuxMatIdx|SmthGrp \n")
while counter < recCount:
counter = counter + 1
Bastien Montagne
committed
indata = unpack('hhhbbi', pskfile.read(12))
printlog(str(indata[0]) + "|" + str(indata[1]) + "|" + str(indata[2]) + "|" + str(indata[3]) + "|" +
str(indata[4]) + "|" + str(indata[5]) + "\n")
#indata[0] = index of UVCoords
#UVCoords[indata[0]]=[index to PNTS, U coord, v coord]
#UVCoords[indata[0]][0] = index to PNTS
PNTSA = UVCoords[indata[2]][0]
PNTSB = UVCoords[indata[1]][0]
PNTSC = UVCoords[indata[0]][0]
Bastien Montagne
committed
#print(PNTSA, PNTSB, PNTSC) #face id vertex
#faces.extend([0, 1, 2, 0])
faces.extend([(PNTSA, PNTSB, PNTSC, 0)])
uv = []
u0 = UVCoords[indata[2]][1]
v0 = UVCoords[indata[2]][2]
Bastien Montagne
committed
uv.append([u0, 1.0 - v0])
u1 = UVCoords[indata[1]][1]
v1 = UVCoords[indata[1]][2]
Bastien Montagne
committed
uv.append([u1, 1.0 - v1])
u2 = UVCoords[indata[0]][1]
v2 = UVCoords[indata[0]][2]
Bastien Montagne
committed
uv.append([u2, 1.0 - v2])
faceuv.append([uv, indata[3], indata[4], indata[5]])
#print("material:", indata[3])
#print("UV: ", u0, v0)
#update the uv var of the last item in the Tmsh.faces list
# which is the face just added above
Bastien Montagne
committed
##Tmsh.faces[-1].uv = [(u0, v0), (u1, v1), (u2, v2)]
#print("smooth:",indata[5])
#collect a list of the smoothing groups
facesmooth.append(indata[5])
#print(indata[5])
if SGlist.count(indata[5]) == 0:
SGlist.append(indata[5])
Bastien Montagne
committed
print("smooth:", indata[5])
#assign a material index to the face
#Tmsh.faces[-1].materialIndex = SGlist.index(indata[5])
Bastien Montagne
committed
printlog("Using Materials to represent PSK Smoothing Groups...\n")
#==========
# skip something...
#==========
Bastien Montagne
committed
#==================================================================================================
# Material
#==================================================================================================
##
#read the MATT0000 header
Bastien Montagne
committed
indata = unpack('20s3i', pskfile.read(32))
recCount = indata[3]
printlog("Nbr of MATT0000 records: " + str(recCount) + "\n" )
printlog(" - Not importing any material data now. PSKs are texture wrapped! \n")
counter = 0
materialcount = 0
while counter < recCount:
counter = counter + 1
Bastien Montagne
committed
indata = unpack('64s6i', pskfile.read(88))
materialcount += 1
Bastien Montagne
committed
print("Material", counter)
print("Mat name %s", indata[0])
##
#==================================================================================================
# Bones (Armature)
#==================================================================================================
#read the REFSKEL0 header
Bastien Montagne
committed
indata = unpack('20s3i', pskfile.read(32))
recCount = indata[3]
printlog( "Nbr of REFSKEL0 records: " + str(recCount) + "\n")
#REFSKEL0 fields - Name|Flgs|NumChld|PrntIdx|Qw|Qx|Qy|Qz|LocX|LocY|LocZ|Lngth|XSize|YSize|ZSize
Bastien Montagne
committed
Bns = []
bone = []
Bastien Montagne
committed
md5_bones = []
bni_dict = {}
#==================================================================================================
# Bone Data
#==================================================================================================
counter = 0
print ("---PRASE--BONES---")
printlog("Name|Flgs|NumChld|PrntIdx|Qx|Qy|Qz|Qw|LocX|LocY|LocZ|Lngth|XSize|YSize|ZSize\n")
while counter < recCount:
Bastien Montagne
committed
indata = unpack('64s3i11f', pskfile.read(120))
#print( "DATA",str(indata))
Bastien Montagne
committed
bone.append(indata)
Bastien Montagne
committed
createbone = md5_bone()
#temp_name = indata[0][:30]
temp_name = indata[0]
temp_name = bytes.decode(temp_name)
temp_name = temp_name.lstrip(" ")
temp_name = temp_name.rstrip(" ")
temp_name = temp_name.strip()
temp_name = temp_name.strip( bytes.decode(b'\x00'))
Bastien Montagne
committed
printlog(temp_name + "|" + str(indata[1]) + "|" + str(indata[2]) + "|" + str(indata[3]) + "|" +
str(indata[4]) + "|" + str(indata[5]) + "|" + str(indata[6]) + "|" + str(indata[7]) + "|" +
str(indata[8]) + "|" + str(indata[9]) + "|" + str(indata[10]) + "|" + str(indata[11]) + "|" +
str(indata[12]) + "|" + str(indata[13]) + "|" + str(indata[14]) + "\n")
createbone.name = temp_name
createbone.bone_index = counter
createbone.parent_index = indata[3]
createbone.bindpos[0] = indata[8]
createbone.bindpos[1] = indata[9]
createbone.bindpos[2] = indata[10]
createbone.scale[0] = indata[12]
createbone.scale[1] = indata[13]
createbone.scale[2] = indata[14]
Bastien Montagne
committed
bni_dict[createbone.name] = createbone.bone_index
Bastien Montagne
committed
#w,x,y,z
if (counter == 0):#main parent
Bastien Montagne
committed
createbone.bindmat = mathutils.Quaternion((indata[7], -indata[4], -indata[5], -indata[6])).to_matrix()
createbone.origmat = mathutils.Quaternion((indata[7], -indata[4], -indata[5], -indata[6])).to_matrix()
Bastien Montagne
committed
createbone.bindmat = mathutils.Quaternion((indata[7], -indata[4], -indata[5], -indata[6])).to_matrix()
createbone.origmat = mathutils.Quaternion((indata[7], -indata[4], -indata[5], -indata[6])).to_matrix()
Bastien Montagne
committed
createbone.bindmat = mathutils.Matrix.Translation(mathutils.Vector((indata[8], indata[9], indata[10]))) * \
createbone.bindmat.to_4x4()
md5_bones.append(createbone)
counter = counter + 1
bnstr = (str(indata[0]))
Bns.append(bnstr)
Bastien Montagne
committed
for pbone in md5_bones:
Bastien Montagne
committed
pbone.parent = md5_bones[pbone.parent_index]
for pbone in md5_bones:
if pbone.name != pbone.parent.name:
Bastien Montagne
committed
pbone.bindmat = pbone.parent.bindmat * pbone.bindmat
#print(pbone.name)
#print(pbone.bindmat)
#print("end")
else:
pbone.bindmat = pbone.bindmat
Bastien Montagne
committed
for pbone in md5_bones:
Bastien Montagne
committed
pbone.head = getheadpos(pbone, md5_bones)
for pbone in md5_bones:
Bastien Montagne
committed
pbone.tail = gettailpos(pbone, md5_bones)
for pbone in md5_bones:
pbone.parent = md5_bones[pbone.parent_index].name
bonecount = 0
for armbone in bone:
temp_name = armbone[0][:30]
Bastien Montagne
committed
#print ("BONE NAME: ", len(temp_name))
temp_name=str((temp_name))
#temp_name = temp_name[1]
Bastien Montagne
committed
#print ("BONE NAME: ", temp_name)
bonecount += 1
print ("-------------------------")
print ("----Creating--Armature---")
print ("-------------------------")
Bastien Montagne
committed
#================================================================================================
#Check armature if exist if so create or update or remove all and addnew bone
#================================================================================================
#bpy.ops.object.mode_set(mode='OBJECT')
meshname ="ArmObject"
objectname = "armaturedata"
# arm = None # UNUSED
if importbone:
obj = bpy.data.objects.get(meshname)
# arm = obj # UNUSED
if not obj:
armdata = bpy.data.armatures.new(objectname)
ob_new = bpy.data.objects.new(meshname, armdata)
#ob_new = bpy.data.objects.new(meshname, 'ARMATURE')
#ob_new.data = armdata
bpy.context.scene.objects.link(ob_new)
#bpy.ops.object.mode_set(mode='OBJECT')
Bastien Montagne
committed
for i in bpy.context.scene.objects:
i.select = False #deselect all objects
ob_new.select = True
#set current armature to edit the bone
bpy.context.scene.objects.active = ob_new
#set mode to able to edit the bone
if bpy.ops.object.mode_set.poll():
bpy.ops.object.mode_set(mode='EDIT')
#newbone = ob_new.data.edit_bones.new('test')
#newbone.tail.y = 1
print("creating bone(s)")
bpy.ops.object.mode_set(mode='OBJECT')
for bone in md5_bones:
#print(dir(bone))
bpy.ops.object.mode_set(mode='EDIT')#Go to edit mode for the bones
newbone = ob_new.data.edit_bones.new(bone.name)
#parent the bone
Bastien Montagne
committed
#print("DRI:", dir(newbone))
parentbone = None
#note bone location is set in the real space or global not local
bonesize = bpy.types.Scene.unrealbonesize
if bone.name != bone.parent:
pos_x = bone.bindpos[0]
pos_y = bone.bindpos[1]
pos_z = bone.bindpos[2]
Bastien Montagne
committed
#print("LINKING:" , bone.parent ,"j")
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
parentbone = ob_new.data.edit_bones[bone.parent]
newbone.parent = parentbone
rotmatrix = bone.bindmat
newbone.head.x = bone.head[0]
newbone.head.y = bone.head[1]
newbone.head.z = bone.head[2]
newbone.tail.x = bone.tail[0]
newbone.tail.y = bone.tail[1]
newbone.tail.z = bone.tail[2]
vecp = parentbone.tail - parentbone.head
vecc = newbone.tail - newbone.head
vecc.normalize()
vecp.normalize()
if vecp.dot(vecc) > -0.8:
newbone.roll = parentbone.roll
else:
newbone.roll = - parentbone.roll
else:
rotmatrix = bone.bindmat
newbone.head.x = bone.head[0]
newbone.head.y = bone.head[1]
newbone.head.z = bone.head[2]
newbone.tail.x = bone.tail[0]
newbone.tail.y = bone.tail[1]
newbone.tail.z = bone.tail[2]
newbone.roll = math.radians(90.0)
"""
vec = newbone.tail - newbone.head
if vec.z > 0.0:
newbone.roll = math.radians(90.0)
else:
newbone.roll = math.radians(-90.0)
"""
bpy.context.scene.update()
Bastien Montagne
committed
#==================================================================================================
#END BONE DATA BUILD
#==================================================================================================
VtxCol = []
for x in range(len(Bns)):
#change the overall darkness of each material in a range between 0.1 and 0.9
Bastien Montagne
committed
tmpVal = ((float(x) + 1.0) / (len(Bns)) * 0.7) + 0.1
tmpVal = int(tmpVal * 256)
Bastien Montagne
committed
tmpCol = [tmpVal, tmpVal, tmpVal, 0]
#Change the color of each material slightly
if x % 3 == 0:
Bastien Montagne
committed
if tmpCol[0] < 128:
tmpCol[0] += 60
else:
tmpCol[0] -= 60
if x % 3 == 1:
Bastien Montagne
committed
if tmpCol[1] < 128:
tmpCol[1] += 60
else:
tmpCol[1] -= 60
if x % 3 == 2:
Bastien Montagne
committed
if tmpCol[2] < 128:
tmpCol[2] += 60
else:
tmpCol[2] -= 60
#Add the material to the mesh
VtxCol.append(tmpCol)
Bastien Montagne
committed
#==================================================================================================
# Bone Weight
#==================================================================================================
#read the RAWW0000 header
Bastien Montagne
committed
indata = unpack('20s3i', pskfile.read(32))
recCount = indata[3]
Bastien Montagne
committed
printlog("Nbr of RAWW0000 records: " + str(recCount) +"\n")
#RAWW0000 fields: Weight|PntIdx|BoneIdx
RWghts = []
counter = 0
while counter < recCount:
counter = counter + 1
Bastien Montagne
committed
indata = unpack('fii', pskfile.read(12))
RWghts.append([indata[1], indata[2], indata[0]])
#print("weight:", [indata[1], indata[2], indata[0]])
#RWghts fields = PntIdx|BoneIdx|Weight
RWghts.sort()
Bastien Montagne
committed
printlog("Vertex point and groups count =" + str(len(RWghts)) + "\n")
printlog("PntIdx|BoneIdx|Weight")
for vg in RWghts:
Bastien Montagne
committed
printlog(str(vg[0]) + "|" + str(vg[1]) + "|" + str(vg[2]) + "\n")
#Tmsh.update_tag()
Bastien Montagne
committed
#set the Vertex Colors of the faces
#face.v[n] = RWghts[0]
#RWghts[1] = index of VtxCol
"""
for x in range(len(Tmsh.faces)):
for y in range(len(Tmsh.faces[x].v)):
#find v in RWghts[n][0]
findVal = Tmsh.faces[x].v[y].index
n = 0
while findVal != RWghts[n][0]:
n = n + 1
TmpCol = VtxCol[RWghts[n][1]]
#check if a vertex has more than one influence
Bastien Montagne
committed
if n != len(RWghts) - 1:
if RWghts[n][0] == RWghts[n + 1][0]:
#if there is more than one influence, use the one with the greater influence
#for simplicity only 2 influences are checked, 2nd and 3rd influences are usually very small
Bastien Montagne
committed
if RWghts[n][2] < RWghts[n + 1][2]:
TmpCol = VtxCol[RWghts[n + 1][1]]
Tmsh.faces[x].col.append(NMesh.Col(TmpCol[0], TmpCol[1], TmpCol[2], 0))
"""
if (DEBUGLOG):
logf.close()
#==================================================================================================
#Building Mesh
#==================================================================================================
Bastien Montagne
committed
print("vertex:", len(verts), "faces:", len(faces))
print("vertex2:", len(verts2))
me_ob.vertices.add(len(verts2))
me_ob.tessfaces.add(len(faces))
me_ob.vertices.foreach_set("co", unpack_list(verts2))
Bastien Montagne
committed
me_ob.tessfaces.foreach_set("vertices_raw", unpack_list( faces))
for face in me_ob.tessfaces:
Bastien Montagne
committed
face.use_smooth = facesmooth[face.index]
"""
Material setup coding.
First the mesh has to be create first to get the uv texture setup working.
-Create material(s) list in the psk pack data from the list.(to do list)
-Append the material to the from create the mesh object.
-Create Texture(s)
Bastien Montagne
committed
-face loop for uv assign and assign material index
"""
bpy.ops.object.mode_set(mode='OBJECT')
#===================================================================================================
#Material Setup
#===================================================================================================
print ("-------------------------")
print ("----Creating--Materials--")
print ("-------------------------")
materialname = "pskmat"
materials = []
for matcount in range(materialcount):
#if texturedata != None:
matdata = bpy.data.materials.new(materialname + str(matcount))
#mtex = matdata.texture_slots.new()
#mtex.texture = texture[matcount].data
#print(type(texture[matcount].data))
#print(dir(mtex))
#print(dir(matdata))
#for texno in range(len( bpy.data.textures)):
Bastien Montagne
committed
#print((bpy.data.textures[texno].name))
#print(dir(bpy.data.textures[texno]))
Bastien Montagne
committed
#matdata.active_texture = bpy.data.textures[matcount - 1]
#matdata.texture_coords = 'UV'
#matdata.active_texture = texturedata
materials.append(matdata)
for material in materials:
#add material to the mesh list of materials
me_ob.materials.append(material)
#===================================================================================================
#UV Setup
#===================================================================================================
print ("-------------------------")
print ("-- Creating UV Texture --")
print ("-------------------------")
texture = []
# texturename = "text1" # UNUSED
countm = 0
for countm in range(materialcount):
Bastien Montagne
committed
psktexname = "psk" + str(countm)
me_ob.uv_textures.new(name=psktexname)
countm += 1
print("INIT UV TEXTURE...")
_matcount = 0
#for mattexcount in materials:
Bastien Montagne
committed
#print("MATERAIL ID:", _matcount)
_textcount = 0
for uv in me_ob.tessface_uv_textures: # uv texture
print("UV TEXTURE ID:",_textcount)
print(dir(uv))
for face in me_ob.tessfaces:# face, uv
#print(dir(face))
if faceuv[face.index][1] == _textcount: #if face index and texture index matches assign it
mfaceuv = faceuv[face.index] #face index
_uv1 = mfaceuv[0][0] #(0,0)
Bastien Montagne
committed
uv.data[face.index].uv1 = mathutils.Vector((_uv1[0], _uv1[1])) #set them
_uv2 = mfaceuv[0][1] #(0,0)
Bastien Montagne
committed
uv.data[face.index].uv2 = mathutils.Vector((_uv2[0], _uv2[1])) #set them
_uv3 = mfaceuv[0][2] #(0,0)
Bastien Montagne
committed
uv.data[face.index].uv3 = mathutils.Vector((_uv3[0], _uv3[1])) #set them
else: #if not match zero them
Bastien Montagne
committed
uv.data[face.index].uv1 = mathutils.Vector((0, 0)) #zero them
uv.data[face.index].uv2 = mathutils.Vector((0, 0)) #zero them
uv.data[face.index].uv3 = mathutils.Vector((0, 0)) #zero them
_textcount += 1
#_matcount += 1
#print(matcount)
print("END UV TEXTURE...")
Bastien Montagne
committed
print("UV TEXTURE LEN:", len(texture))
#for tex in me_ob.uv_textures:
Bastien Montagne
committed
#print("mesh tex:", dir(tex))
#print((tex.name))
Bastien Montagne
committed
#for face in me_ob.faces:
#print(dir(face))
#===================================================================================================
#
#===================================================================================================
obmesh = bpy.data.objects.new(objName,me_ob)
#===================================================================================================
#Mesh Vertex Group bone weight
#===================================================================================================
print("---- building bone weight mesh ----")
#print(dir(ob_new.data.bones))
#create bone vertex group #deal with bone id for index number
for bone in ob_new.data.bones:
Bastien Montagne
committed
#print("names:", bone.name, ":", dir(bone))
#print("names:", bone.name)
group = obmesh.vertex_groups.new(bone.name)
for vgroup in obmesh.vertex_groups:
Bastien Montagne
committed
#print(vgroup.name, ":", vgroup.index)
for vgp in RWghts:
#bone index
if vgp[1] == bni_dict[vgroup.name]:
#print(vgp)
#[vertex id],weight
vgroup.add([vgp[0]], vgp[2], 'ADD')
#check if there is a material to set to
if len(materials) > 0:
obmesh.active_material = materials[0] #material setup tmp
print("---- adding mesh to the scene ----")
Bastien Montagne
committed
bpy.ops.object.mode_set(mode='OBJECT')
#bpy.ops.object.select_pattern(extend=True, pattern=obmesh.name, case_sensitive=True)
#bpy.ops.object.select_pattern(extend=True, pattern=ob_new.name, case_sensitive=True)
#bpy.ops.object.select_name(name=str(obmesh.name))
#bpy.ops.object.select_name(name=str(ob_new.name))
#bpy.context.scene.objects.active = ob_new
me_ob.update()
bpy.context.scene.objects.link(obmesh)
bpy.context.scene.update()
obmesh.select = False
ob_new.select = False
obmesh.select = True
ob_new.select = True
bpy.ops.object.parent_set(type="ARMATURE")
Bastien Montagne
committed
print ("PSK2Blender completed")
#End of def pskimport#########################
Bastien Montagne
committed
def getInputFilenamepsk(self, filename, importmesh, importbone, bDebugLogPSK, importmultiuvtextures):
checktype = filename.split('\\')[-1].split('.')[1]
print ("------------",filename)
if checktype.lower() != 'psk':
Bastien Montagne
committed
print (" Selected file = ", filename)
raise (IOError, "The selected input file is not a *.psk file")
#self.report({'INFO'}, ("Selected file:"+ filename))
else:
Bastien Montagne
committed
pskimport(filename, importmesh, importbone, bDebugLogPSK, importmultiuvtextures)
def getInputFilenamepsa(self, filename, context):
checktype = filename.split('\\')[-1].split('.')[1]
if checktype.lower() != 'psa':
Bastien Montagne
committed
print (" Selected file = ", filename)
raise (IOError, "The selected input file is not a *.psa file")
Bastien Montagne
committed
#self.report({'INFO'}, ("Selected file:" + filename))
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
else:
psaimport(filename,context)
class IMPORT_OT_psk(bpy.types.Operator):
'''Load a skeleton mesh psk File'''
bl_idname = "import_scene.psk"
bl_label = "Import PSK"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_options = {'UNDO'}
# List of operator properties, the attributes will be assigned
# to the class instance from the operator settings before calling.
filepath = StringProperty(
subtype='FILE_PATH',
)
filter_glob = StringProperty(
default="*.psk",
options={'HIDDEN'},
)
importmesh = BoolProperty(
name="Mesh",
description="Import mesh only. (not yet build.)",
default=True,
)
importbone = BoolProperty(
name="Bones",
description="Import bones only. Current not working yet",
default=True,
)
importmultiuvtextures = BoolProperty(
name="Single UV Texture(s)",
description="Single or Multi uv textures",
default=True,
)
bDebugLogPSK = BoolProperty(
name="Debug Log.txt",
Bastien Montagne
committed
description="Log the output of raw format. It will save in "
"current file dir. Note this just for testing",
default=False,
)
unrealbonesize = FloatProperty(
name="Bone Length",
description="Bone Length from head to tail distance",
default=1,
min=0.001,
max=1000,
)
def execute(self, context):
bpy.types.Scene.unrealbonesize = self.unrealbonesize
Bastien Montagne
committed
getInputFilenamepsk(self, self.filepath, self.importmesh, self.importbone, self.bDebugLogPSK,
self.importmultiuvtextures)
return {'FINISHED'}
def invoke(self, context, event):
wm = context.window_manager
wm.fileselect_add(self)
Bastien Montagne
committed
return {'RUNNING_MODAL'}
class psa_bone:
name=""
Transform=None
parent=None
def __init__(self):
self.name=""
self.Transform=None
self.parent=None
Bastien Montagne
committed
def psaimport(filename,context):
print ("--------------------------------------------------")
print ("---------SCRIPT EXECUTING PYTHON IMPORTER---------")
print ("--------------------------------------------------")
print ("Importing file: ", filename)
psafile = open(filename,'rb')
debug = True
if (debug):
logpath = filename.replace(".psa", ".txt")
Bastien Montagne
committed
print("logpath:", logpath)
logf = open(logpath, 'w')
def printlog(strdata):
if (debug):
logf.write(strdata)
Bastien Montagne
committed
def printlogplus(name, data):
if (debug):
Bastien Montagne
committed
logf.write(str(name) + '\n')
if isinstance(data, bytes):
logf.write(str(bytes.decode(data).strip(bytes.decode(b'\x00'))))
else:
logf.write(str(data))
logf.write('\n')
Bastien Montagne
committed
printlog('-----------Log File------------\n')
#General Header
Bastien Montagne
committed
indata = unpack('20s3i', psafile.read(32))
printlogplus('ChunkID', indata[0])
printlogplus('TypeFlag', indata[1])
printlogplus('DataSize', indata[2])
printlogplus('DataCount', indata[3])
#Bones Header
Bastien Montagne
committed
indata = unpack('20s3i', psafile.read(32))
printlogplus('ChunkID', indata[0])
printlogplus('TypeFlag', indata[1])
printlogplus('DataSize', indata[2])
printlogplus('DataCount', indata[3])
#Bones Data
BoneIndex2NamePairMap = {}
BoneNotFoundList = []
printlog("Name|Flgs|NumChld|PrntIdx|Qx|Qy|Qz|Qw|LocX|LocY|LocZ|Length|XSize|YSize|ZSize\n")
recCount = indata[3]
counter = 0
nobonematch = True
while counter < recCount:
Bastien Montagne
committed
indata = unpack('64s3i11f', psafile.read(120))
#printlogplus('bone', indata[0])
bonename = str(bytes.decode(indata[0]).strip(bytes.decode(b'\x00')))
if bonename in bpy.data.armatures['armaturedata'].bones.keys():
BoneIndex2NamePairMap[counter] = bonename
Bastien Montagne
committed
print('find bone', bonename)
nobonematch = False
else:
Bastien Montagne
committed
print('can not find the bone:', bonename)
BoneNotFoundList.append(counter)
counter += 1
Bastien Montagne
committed
if nobonematch:
print('no bone was match so skip import!')
return
Bastien Montagne
committed
#Animations Header
Bastien Montagne
committed
indata = unpack('20s3i', psafile.read(32))
printlogplus('ChunkID', indata[0])
printlogplus('TypeFlag', indata[1])
printlogplus('DataSize', indata[2])
printlogplus('DataCount', indata[3])
#Animations Data
recCount = indata[3]
counter = 0
Raw_Key_Nums = 0
Action_List = []
while counter < recCount:
Bastien Montagne
committed
indata = unpack('64s64s4i3f3i', psafile.read(64 + 64 + 4 * 4 + 3 * 4 + 3 * 4))
printlogplus('Name', indata[0])
printlogplus('Group', indata[1])
printlogplus('totalbones', indata[2])
printlogplus('NumRawFrames', indata[-1])
Name = str(bytes.decode(indata[0]).strip(bytes.decode(b'\x00')))
Group = str(bytes.decode(indata[1]).strip(bytes.decode(b'\x00')))
totalbones = indata[2]
NumRawFrames = indata[-1]
Bastien Montagne
committed
Raw_Key_Nums += indata[2] * indata[-1]
Action_List.append((Name,Group,totalbones,NumRawFrames))
Bastien Montagne
committed
counter += 1
Bastien Montagne
committed
#Raw keys Header
Raw_Key_List = []
Bastien Montagne
committed
indata = unpack('20s3i', psafile.read(32))
printlogplus('ChunkID', indata[0])
printlogplus('TypeFlag', indata[1])
printlogplus('DataSize', indata[2])
printlogplus('DataCount', indata[3])
if(Raw_Key_Nums != indata[3]):
print('error! Raw_Key_Nums Inconsistent')
return
#Raw keys Data
recCount = Raw_Key_Nums
counter = 0
while counter < recCount:
Bastien Montagne
committed
indata = unpack('3f4f1f', psafile.read(3 * 4 + 4 * 4 + 4))
pos = mathutils.Vector((indata[0], indata[1], indata[2]))
quat = mathutils.Quaternion((indata[6], indata[3], indata[4], indata[5]))
time = indata[7]
Bastien Montagne
committed
Raw_Key_List.append((pos, quat, time))
counter += 1
#Scale keys Header,Scale keys Data,Curve keys Header,Curve keys Data
curFilePos = psafile.tell()
Bastien Montagne
committed
psafile.seek(0, 2)
endFilePos = psafile.tell()
if curFilePos == endFilePos:
print('no Scale keys,Curve keys')
Bastien Montagne
committed
#build the animation line
if bpy.ops.object.mode_set.poll():
bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
Bastien Montagne
committed
NeededBoneMatrix = {}
ARMATURE_OBJ = 'ArmObject'
ARMATURE_DATA = 'armaturedata'
if bpy.context.scene.udk_importarmatureselect:
if len(bpy.context.scene.udkas_list) > 0:
print("CHECKING ARMATURE...")
Bastien Montagne
committed
#for bone in bpy.data.objects[ARMATURE_OBJ].pose.bones:
Bastien Montagne
committed
#print("NAME:", objd.name, " TYPE:", objd.type)
#if objd.type == 'ARMARURE':
#print(dir(objd))
armature_list = bpy.context.scene.udkas_list #armature list array
armature_idx = bpy.context.scene.udkimportarmature_list_idx #armature index selected
ARMATURE_OBJ = bpy.data.objects[armature_list[armature_idx]].name #object armature
ARMATURE_DATA = bpy.data.objects[armature_list[armature_idx]].data.name #object data
Bastien Montagne
committed
for bone in bpy.data.armatures[ARMATURE_DATA].bones:
name = bone.name
ori_matrix = bone.matrix
matrix = bone.matrix_local.to_3x3()
bone_rest_matrix = Matrix(matrix)
#bone_rest_matrix = bone.matrix_local.to_3x3()
#bone_rest_matrix = bone.matrix_local.to_quaternion().conjugated().to_matrix()
bone_rest_matrix_inv = Matrix(bone_rest_matrix)
bone_rest_matrix_inv.invert()
bone_rest_matrix_inv.resize_4x4()
bone_rest_matrix.resize_4x4()
NeededBoneMatrix[name] = (bone_rest_matrix,bone_rest_matrix_inv,ori_matrix)
Bastien Montagne
committed
#build tmp pose bone tree
psa_bones = {}
for bone in bpy.data.objects[ARMATURE_OBJ].pose.bones:
_psa_bone = psa_bone()
_psa_bone.name = bone.name
_psa_bone.Transform = bone.matrix
if bone.parent != None:
_psa_bone.parent = psa_bones[bone.parent.name]
else:
_psa_bone.parent = None
psa_bones[bone.name] = _psa_bone
Bastien Montagne
committed
raw_key_index = 0
for raw_action in Action_List:
Bastien Montagne
committed
Name = raw_action[0]
Group = raw_action[1]
Totalbones = raw_action[2]
NumRawFrames = raw_action[3]
context.scene.update()
object = bpy.data.objects['ArmObject']
object.animation_data_create()
Bastien Montagne
committed
action = bpy.data.actions.new(name=Name)
object.animation_data.action = action
for i in range(NumRawFrames):
Bastien Montagne
committed
context.scene.frame_set(i + 1)
pose_bones = object.pose.bones
for j in range(Totalbones):
if j not in BoneNotFoundList:
bName = BoneIndex2NamePairMap[j]
Bastien Montagne
committed
pbone = psa_bones[bName]
pos = Raw_Key_List[raw_key_index][0]
quat = Raw_Key_List[raw_key_index][1]
Bastien Montagne
committed
mat = Matrix()
if pbone.parent != None:
quat = quat.conjugated()
mat = Matrix.Translation(pos) * quat.to_matrix().to_4x4()
mat = pose_bones[bName].parent.matrix * mat
#mat = pbone.parent.Transform * mat
else:
mat = pbone.Transform * Matrix.Translation(pos) * quat.to_matrix().to_4x4()
pose_bones[bName].matrix = mat
pbone.Transform = mat
raw_key_index += 1
#bpy.data.meshes[1]
Bastien Montagne
committed
for bone in pose_bones:
bone.matrix = psa_bones[bone.name].Transform
bone.keyframe_insert("rotation_quaternion")
bone.keyframe_insert("location")
def whirlSingleBone(pose_bone,quat):
bpy.context.scene.update()
#record child's matrix and origin rotate