Skip to content

Instantly share code, notes, and snippets.

@flufy3d
Created September 26, 2012 10:13
Show Gist options
  • Save flufy3d/3787175 to your computer and use it in GitHub Desktop.
Save flufy3d/3787175 to your computer and use it in GitHub Desktop.
blender import psk
# ##### 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)",
"author": "Darknet",
"version": (2, 0),
"blender": (2, 5, 9),
"location": "File > Import > Skeleton Mesh (.psk)",
"description": "Import Skeleleton Mesh",
"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
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",
default=1,min=0.001,max=1000)
#output log in to txt file
DEBUGLOG = False
scale = 1.0
bonesize = 1.0
def unpack_list(list_of_tuples):
l = []
for t in list_of_tuples:
l.extend(t)
return l
"""
class md5_bone:
bone_index=0
name=""
bindpos=[]
bindmat = mathutils.Quaternion()
parent=""
parent_index=0
blenderbone=None
roll=0
def __init__(self):
self.bone_index=0
self.name=""
self.bindpos=[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.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)
"""
class md5_bone:
bone_index=0
name=""
bindpos=[]
bindmat=[]
origmat=[]
head=[]
tail=[]
scale = []
parent=""
parent_index=0
blenderbone=None
roll=0
def __init__(self):
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)
#http://www.blender.org/forum/viewtopic.php?t=13340&sid=8b17d5de07b17960021bbd72cac0495f
def fixRollZ(b):
if b.length != 0:
v = (b.tail-b.head)/b.length
b.roll -= math.degrees(math.atan2(v[0]*v[2]*(1 - v[1]),v[0]*v[0] + v[1]*v[2]*v[2]))
def fixRoll(b):
if b.length == 0:
return
v = (b.tail-b.head)/b.length
if v[2]*v[2] > .5:
#align X-axis
b.roll += math.degrees(math.atan2(v[0]*v[2]*(1 - v[1]),v[2]*v[2] + v[1]*v[0]*v[0]))
else:
#align Z-axis
b.roll -= math.degrees(math.atan2(v[0]*v[2]*(1 - v[1]),v[0]*v[0] + v[1]*v[2]*v[2]))
def getheadpos(pbone,bones):
pos_head = [0.0]*3
#pos = mathutils.Vector((x,y,z)) * pbone.origmat
pos = pbone.bindmat.to_translation()
"""
tmp_bone = pbone
while tmp_bone.name != tmp_bone.parent.name:
pos = pos * tmp_bone.parent.bindmat
tmp_bone = tmp_bone.parent
"""
pos_head[0] = pos.x
pos_head[1] = pos.y
pos_head[2] = pos.z
return pos_head
def gettailpos(pbone,bones):
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:
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
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)
pskfile = open(infile,'rb')
if (DEBUGLOG):
logpath = infile.replace(".psk", ".txt")
print("logpath:",logpath)
logf = open(logpath,'w')
def printlog(strdata):
if (DEBUGLOG):
logf.write(strdata)
objName = infile.split('\\')[-1].split('.')[0]
me_ob = bpy.data.meshes.new(objName)
print("objName:",objName)
printlog(("New Mesh = " + me_ob.name + "\n"))
#read general header
indata = unpack('20s3i',pskfile.read(32))
#not using the general header at this time
#==================================================================================================
# vertex point
#==================================================================================================
#read the PNTS0000 header
indata = unpack('20s3i',pskfile.read(32))
recCount = indata[3]
printlog(( "Nbr of PNTS0000 records: " + str(recCount) + "\n"))
counter = 0
verts = []
while counter < recCount:
counter = counter + 1
indata = unpack('3f',pskfile.read(12))
#print(indata[0],indata[1],indata[2])
verts.extend([(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
indata = unpack('20s3i',pskfile.read(32))
recCount = indata[3]
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
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
indata = unpack('20s3i',pskfile.read(32))
recCount = indata[3]
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 = []
#the psk values are: nWdgIdx1|WdgIdx2|WdgIdx3|MatIdx|AuxMatIdx|SmthGrp
printlog("nWdgIdx1|WdgIdx2|WdgIdx3|MatIdx|AuxMatIdx|SmthGrp \n")
while counter < recCount:
counter = counter + 1
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]
#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]
uv.append([u0,1.0 - v0])
u1 = UVCoords[indata[1]][1]
v1 = UVCoords[indata[1]][2]
uv.append([u1,1.0 - v1])
u2 = UVCoords[indata[0]][1]
v2 = UVCoords[indata[0]][2]
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
##Tmsh.faces[-1].uv = [(u0,v0),(u1,v1),(u2,v2)]
#print("smooth:",indata[5])
#collect a list of the smoothing groups
if SGlist.count(indata[5]) == 0:
SGlist.append(indata[5])
print("smooth:",indata[5])
#assign a material index to the face
#Tmsh.faces[-1].materialIndex = SGlist.index(indata[5])
printlog( "Using Materials to represent PSK Smoothing Groups...\n")
#==========
# skip something...
#==========
#==================================================================================================
# Material
#==================================================================================================
##
#read the MATT0000 header
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
indata = unpack('64s6i',pskfile.read(88))
materialcount += 1
print("Material",counter)
print("Mat name %s",indata[0])
##
#==================================================================================================
# Bones (Armature)
#==================================================================================================
#read the REFSKEL0 header
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
Bns = []
bone = []
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:
indata = unpack('64s3i11f',pskfile.read(120))
#print( "DATA",str(indata))
bone.append(indata)
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'))
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]
bni_dict[createbone.name] = createbone.bone_index
#w,x,y,z
if (counter == 0):#main parent
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()
else:
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()
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)
for pbone in md5_bones:
pbone.parent = md5_bones[pbone.parent_index]
for pbone in md5_bones:
if pbone.name != pbone.parent.name:
pbone.bindmat = pbone.parent.bindmat * pbone.bindmat
#print(pbone.name)
#print(pbone.bindmat)
#print("end")
else:
pbone.bindmat = pbone.bindmat
for pbone in md5_bones:
pbone.head = getheadpos(pbone,md5_bones)
for pbone in md5_bones:
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]
#print ("BONE NAME: ",len(temp_name))
temp_name=str((temp_name))
#temp_name = temp_name[1]
#print ("BONE NAME: ",temp_name)
bonecount +=1
print ("-------------------------")
print ("----Creating--Armature---")
print ("-------------------------")
#================================================================================================
#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')
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
bpy.ops.object.mode_set(mode='EDIT')
#newbone = ob_new.data.edit_bones.new('test')
#newbone.tail.y = 1
print("creating bone(s)")
for bone in md5_bones:
#print(dir(bone))
bpy.ops.object.mode_set(mode='EDIT')
newbone = ob_new.data.edit_bones.new(bone.name)
'''
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')
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)")
for bone in md5_bones:
#print(dir(bone))
newbone = ob_new.data.edit_bones.new(bone.name)
#parent the bone
#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]
#print( "LINKING:" , bone.parent ,"j")
parentbone = ob_new.data.edit_bones[bone.parent]
newbone.parent = parentbone
rotmatrix = bone.bindmat
#parentbone.head.x
#newbone.head.x = pos_x
#newbone.head.y = pos_y
#newbone.head.z = pos_z
#print("head:",newbone.head)
#newbone.tail.x = (pos_x + bonesize * rotmatrix[0][1])
#newbone.tail.y = (pos_y + bonesize * rotmatrix[1][1])
#newbone.tail.z = (pos_z + bonesize * rotmatrix[2][1])
#newbone.roll = fixRoll(newbone)
#newbone.transform(bone.bindmat,True,True)
#newbone.translate(parentbone.head)
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 = bone.bindmat.to_translation()
#newbone.head.x = bone.bindpos[0]
#newbone.head.y = bone.bindpos[1]
#newbone.head.z = bone.bindpos[2]
#newbone.tail.x = bone.bindpos[0] + bonesize * rotmatrix[0][1]
#newbone.tail.y = bone.bindpos[1] + bonesize * rotmatrix[1][1]
#newbone.tail.z = bone.bindpos[2] + bonesize * rotmatrix[2][1]
#newbone.transform(bone.bindmat,True,True)
#newbone.roll = fixRoll(newbone)
#print("no parent")
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()
#==================================================================================================
#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
tmpVal = ((float(x)+1.0)/(len(Bns))*0.7)+0.1
tmpVal = int(tmpVal * 256)
tmpCol = [tmpVal,tmpVal,tmpVal,0]
#Change the color of each material slightly
if x % 3 == 0:
if tmpCol[0] < 128: tmpCol[0] += 60
else: tmpCol[0] -= 60
if x % 3 == 1:
if tmpCol[1] < 128: tmpCol[1] += 60
else: tmpCol[1] -= 60
if x % 3 == 2:
if tmpCol[2] < 128: tmpCol[2] += 60
else: tmpCol[2] -= 60
#Add the material to the mesh
VtxCol.append(tmpCol)
#==================================================================================================
# Bone Weight
#==================================================================================================
#read the RAWW0000 header
indata = unpack('20s3i',pskfile.read(32))
recCount = indata[3]
printlog( "Nbr of RAWW0000 records: " + str(recCount) +"\n")
#RAWW0000 fields: Weight|PntIdx|BoneIdx
RWghts = []
counter = 0
while counter < recCount:
counter = counter + 1
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()
printlog( "Vertex point and groups count =" + str(len(RWghts)) + "\n")
printlog("PntIdx|BoneIdx|Weight")
for vg in RWghts:
printlog( str(vg[0]) + "|" + str(vg[1]) + "|" + str(vg[2]) + "\n")
#Tmsh.update_tag()
#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
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
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
#==================================================================================================
print("vertex:",len(verts),"faces:",len(faces))
me_ob.vertices.add(len(verts))
me_ob.faces.add(len(faces)//4)
me_ob.vertices.foreach_set("co", unpack_list(verts))
me_ob.faces.foreach_set("vertices_raw", faces)
me_ob.faces.foreach_set("use_smooth", [False] * len(me_ob.faces))
me_ob.update_tag()
"""
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)
-fae 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)):
#print((bpy.data.textures[texno].name))
#print(dir(bpy.data.textures[texno]))
#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):
#psktexname="psk" + str(countm)
#me_ob.uv_textures.new(name=psktexname)
if importmultiuvtextures == True:
me_ob.uv_textures.new(name="pskuvtexture")
#print(dir(bpy.data))
if (len(faceuv) > 0):
for countm in range(len(me_ob.uv_textures)):
me_ob.update()
uvtex = me_ob.uv_textures[countm] #add one uv texture
me_ob.update()
#print("UV TEXTURE NAME:",uvtex.name)
for i, face in enumerate(me_ob.faces):
blender_tface = uvtex.data[i] #face
mfaceuv = faceuv[i]
#print("---------------------------------------")
#print(faceuv[i][1])
#print(dir(face))
face.material_index = faceuv[i][1]
blender_tface.uv1 = mfaceuv[0][0] #uv = (0,0)
blender_tface.uv2 = mfaceuv[0][1] #uv = (0,0)
blender_tface.uv3 = mfaceuv[0][2] #uv = (0,0)
texture.append(uvtex)
else:
for countm in range(materialcount):
psktexname="psk" + str(countm)
me_ob.uv_textures.new(name=psktexname)
#psktexname="psk" + str(countm)
#me_ob.uv_textures.new(name=psktexname)
for countm in range(len(me_ob.uv_textures)):
me_ob.update()
#print(dir(me_ob.uv_textures))
#psktexname="psk" + str(countm)
uvtex = me_ob.uv_textures[countm] #add one uv texture
me_ob.update()
#print("UV TEXTURE NAME:",uvtex.name)
if (len(faceuv) > 0):
# counttex = 0 # UNUSED
countm = 0
for countm in range(len(me_ob.uv_textures)):
me_ob.update()
#print(dir(me_ob.uv_textures))
psktexname="psk" + str(countm)
uvtex = me_ob.uv_textures[countm] #add one uv texture
me_ob.update()
#print("UV TEXTURE NAME:",uvtex.name)
for i, face in enumerate(me_ob.faces):
blender_tface = uvtex.data[i] #face
mfaceuv = faceuv[i]
#print("---------------------------------------")
#print(faceuv[i][1])
#print(dir(face))
face.material_index = faceuv[i][1]
if countm == faceuv[i][1]:
face.material_index = faceuv[i][1]
blender_tface.uv1 = mfaceuv[0][0] #uv = (0,0)
blender_tface.uv2 = mfaceuv[0][1] #uv = (0,0)
blender_tface.uv3 = mfaceuv[0][2] #uv = (0,0)
else:
#set uv to zero (0,0)
#print("--------------------")
#print(blender_tface.uv1)
#print(blender_tface.uv2)
#print(blender_tface.uv2)
blender_tface.uv1 = [0,0]
#print(blender_tface.uv1)
blender_tface.uv2 = [0,0]
blender_tface.uv3 = [0,0]
texture.append(uvtex)
print("UV TEXTURE LEN:",len(texture))
#for tex in me_ob.uv_textures:
#print("mesh tex:",dir(tex))
#print((tex.name))
#for face in me_ob.faces:
#print(dir(face))
'''
matdata = bpy.data.materials.new(materialname)
#color is 0 - 1 not in 0 - 255
#matdata.mirror_color=(float(0.04),float(0.08),float(0.44))
matdata.diffuse_color=(float(0.04),float(0.08),float(0.44))#blue color
#print(dir(me_ob.uv_textures[0].data))
texdata = None
texdata = bpy.data.textures[len(bpy.data.textures)-1]
if (texdata != None):
#print(texdata.name)
#print(dir(texdata))
texdata.name = "texturelist1"
matdata.active_texture = texdata
materials.append(matdata)
#matdata = bpy.data.materials.new(materialname)
#materials.append(matdata)
#= make sure the list isnt too big
for material in materials:
#add material to the mesh list of materials
me_ob.materials.append(material)
'''
#===================================================================================================
#
#===================================================================================================
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:
#print("names:",bone.name,":",dir(bone))
#print("names:",bone.name)
group = obmesh.vertex_groups.new(bone.name)
for vgroup in obmesh.vertex_groups:
#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 ----")
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
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")
print ("PSK2Blender completed")
#End of def pskimport#########################
def getInputFilename(self,filename,importmesh,importbone,bDebugLogPSK,importmultiuvtextures):
checktype = filename.split('\\')[-1].split('.')[1]
print ("------------",filename)
if checktype.lower() != 'psk':
print (" Selected file = ",filename)
raise (IOError, "The selected input file is not a *.psk file")
#self.report({'INFO'}, ("Selected file:"+ filename))
else:
pskimport(filename,importmesh,importbone,bDebugLogPSK,importmultiuvtextures)
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",
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
getInputFilename(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)
return {'RUNNING_MODAL'}
def menu_func(self, context):
self.layout.operator(IMPORT_OT_psk.bl_idname, text="Skeleton Mesh (.psk)")
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()
#note this only read the data and will not be place in the scene
#getInputFilename('C:\\blenderfiles\\BotA.psk')
#getInputFilename('C:\\blenderfiles\\AA.PSK')
@flufy3d
Copy link
Author

flufy3d commented Sep 26, 2012

##### 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)",
"author": "Darknet",
"version": (2, 0),
"blender": (2, 5, 9),
"location": "File > Import > Skeleton Mesh (.psk)",
"description": "Import Skeleleton Mesh",
"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
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",
default=1,min=0.001,max=1000)

output log in to txt file

DEBUGLOG = False

scale = 1.0
bonesize = 1.0
md5_bones=[]

def unpack_list(list_of_tuples):
l = []
for t in list_of_tuples:
l.extend(t)
return l
"""
class md5_bone:
bone_index=0
name=""
bindpos=[]
bindmat = mathutils.Quaternion()
parent=""
parent_index=0
blenderbone=None
roll=0

def __init__(self):
    self.bone_index=0
    self.name=""
    self.bindpos=[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.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)

"""
class md5_bone:
bone_index=0
name=""
bindpos=[]
bindmat=[]
scale = []
parent=""
parent_index=0
blenderbone=None
roll=0

def __init__(self):
    self.bone_index=0
    self.name=""
    self.bindpos=[0.0]*3
    self.scale=[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.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)

http://www.blender.org/forum/viewtopic.php?t=13340&sid=8b17d5de07b17960021bbd72cac0495f

def fixRollZ(b):
v = (b.tail-b.head)/b.length
b.roll -= math.degrees(math.atan2(v[0]v[2](1 - v[1]),v[0]_v[0] + v[1]_v[2]_v[2]))
def fixRoll(b):
v = (b.tail-b.head)/b.length
if v[2]_v[2] > .5:
#align X-axis
b.roll += math.degrees(math.atan2(v[0]v[2](1 - v[1]),v[2]_v[2] + v[1]_v[0]_v[0]))
else:
#align Z-axis
b.roll -= math.degrees(math.atan2(v[0]v[2](1 - v[1]),v[0]_v[0] + v[1]_v[2]_v[2]))

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)

md5_bones=[]
pskfile = open(infile,'rb')
if (DEBUGLOG):
    logpath = infile.replace(".psk", ".txt")
    print("logpath:",logpath)
    logf = open(logpath,'w')

def printlog(strdata):
    if (DEBUGLOG):
        logf.write(strdata)

objName = infile.split('\\')[-1].split('.')[0]

me_ob = bpy.data.meshes.new(objName)
print("objName:",objName)
printlog(("New Mesh = " + me_ob.name + "\n"))
#read general header
indata = unpack('20s3i',pskfile.read(32))
#not using the general header at this time
#================================================================================================== 
# vertex point
#================================================================================================== 
#read the PNTS0000 header
indata = unpack('20s3i',pskfile.read(32))
recCount = indata[3]
printlog(( "Nbr of PNTS0000 records: " + str(recCount) + "\n"))
counter = 0
verts = []
while counter < recCount:
    counter = counter + 1
    indata = unpack('3f',pskfile.read(12))
    #print(indata[0],indata[1],indata[2])
    verts.extend([(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
indata = unpack('20s3i',pskfile.read(32))
recCount = indata[3]
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
    indata = unpack('hhffhh',pskfile.read(16))
    print(indata[4],end = " ")
    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
indata = unpack('20s3i',pskfile.read(32))
recCount = indata[3]
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 = []
#the psk values are: nWdgIdx1|WdgIdx2|WdgIdx3|MatIdx|AuxMatIdx|SmthGrp
printlog("nWdgIdx1|WdgIdx2|WdgIdx3|MatIdx|AuxMatIdx|SmthGrp \n")
while counter < recCount:
    counter = counter + 1
    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[0]][0]
    PNTSB = UVCoords[indata[1]][0]
    PNTSC = UVCoords[indata[2]][0]
    #print(PNTSA,PNTSB,PNTSC) #face id vertex
    #faces.extend([0,1,2,0])
    faces.extend([PNTSA,PNTSB,PNTSC,0])
    uv = []
    u0 = UVCoords[indata[0]][1]
    v0 = UVCoords[indata[0]][2]
    uv.append([u0,v0])
    u1 = UVCoords[indata[1]][1]
    v1 = UVCoords[indata[1]][2]
    uv.append([u1,v1])
    u2 = UVCoords[indata[2]][1]
    v2 = UVCoords[indata[2]][2]
    uv.append([u2,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
    ##Tmsh.faces[-1].uv = [(u0,v0),(u1,v1),(u2,v2)]
    #print("smooth:",indata[5])
    #collect a list of the smoothing groups
    if SGlist.count(indata[5]) == 0:
        SGlist.append(indata[5])
        print("smooth:",indata[5])
    #assign a material index to the face
    #Tmsh.faces[-1].materialIndex = SGlist.index(indata[5])
printlog( "Using Materials to represent PSK Smoothing Groups...\n")
#==========
# skip something...
#==========

#================================================================================================== 
# Material
#================================================================================================== 
##
#read the MATT0000 header
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
    indata = unpack('64s6i',pskfile.read(88))
    materialcount += 1
    print("Material",counter)
    print("Mat name %s",indata[0])

##

#================================================================================================== 
# Bones (Armature)
#================================================================================================== 
#read the REFSKEL0 header
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

Bns = []
bone = []
#================================================================================================== 
# 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:
    indata = unpack('64s3i11f',pskfile.read(120))
    #print( "DATA",str(indata))

    bone.append(indata)

    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'))
    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")
    print ("temp_name:", temp_name, "||")
    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]

    #w,x,y,z
    if (counter == 0):#main parent
        print("no parent bone")
        createbone.bindmat = mathutils.Quaternion((indata[7],-indata[4],-indata[5],-indata[6])).normalized().to_matrix()

    else:
        print("parent bone")
        createbone.bindmat = mathutils.Quaternion((indata[7],indata[4],indata[5],indata[6])).normalized().to_matrix()


    md5_bones.append(createbone)
    counter = counter + 1
    bnstr = (str(indata[0]))
    Bns.append(bnstr)

for pbone in md5_bones:
    pbone.parent =  md5_bones[pbone.parent_index]

for pbone in md5_bones:
    if pbone.name != pbone.parent:
        pbone.bindmat = pbone.bindmat * pbone.parent.bindmat
    else:
        pbone.bindmat = pbone.bindmat

for pbone in md5_bones:
    pbone.parent =  md5_bones[pbone.parent_index].name



bonecount = 0
for armbone in bone:
    temp_name = armbone[0][:30]
    #print ("BONE NAME: ",len(temp_name))
    temp_name=str((temp_name))
    #temp_name = temp_name[1]
    #print ("BONE NAME: ",temp_name)
    bonecount +=1
print ("-------------------------")
print ("----Creating--Armature---")
print ("-------------------------")

#================================================================================================
#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')
        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
        bpy.ops.object.mode_set(mode='EDIT')

        #newbone = ob_new.data.edit_bones.new('test')
        #newbone.tail.y = 1
        print("creating bone(s)")
        for bone in md5_bones:
            #print(dir(bone))
            bpy.ops.object.mode_set(mode='EDIT')
            newbone = ob_new.data.edit_bones.new(bone.name)
        '''     


        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')
        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)")
        for bone in md5_bones:
            #print(dir(bone))
            newbone = ob_new.data.edit_bones.new(bone.name)
            #parent the bone
            #print("DRI:",dir(newbone))
            parentbone = None
            print("bone name:",bone.name)
            #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]

                #print( "LINKING:" , bone.parent ,"j")
                parentbone = ob_new.data.edit_bones[bone.parent]
                newbone.parent = parentbone

                rotmatrix = bone.bindmat



                #parentbone.head.x
                newbone.head.x = pos_x
                newbone.head.y = pos_y
                newbone.head.z = pos_z
                #print("head:",newbone.head)
                newbone.tail.x = (pos_x + bonesize * rotmatrix[0][1])
                newbone.tail.y = (pos_y + bonesize * rotmatrix[1][1])
                newbone.tail.z = (pos_z + bonesize * rotmatrix[2][1])
                #newbone.roll = fixRoll(newbone)
                newbone.transform(bone.bindmat,True,True)
                newbone.translate(parentbone.head)





            else:

                rotmatrix = bone.bindmat
                newbone.transform(bone.bindmat,True,True)
                newbone.head.x = bone.bindpos[0]
                newbone.head.y = bone.bindpos[1]
                newbone.head.z = bone.bindpos[2]
                newbone.tail.x = bone.bindpos[0] + bonesize * rotmatrix[0][1]
                newbone.tail.y = bone.bindpos[1] + bonesize * rotmatrix[1][1]
                newbone.tail.z = bone.bindpos[2] + bonesize * rotmatrix[2][1]
                #newbone.roll = fixRoll(newbone)
                #print("no parent")



bpy.context.scene.update()

return

#==================================================================================================
#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
    tmpVal = ((float(x)+1.0)/(len(Bns))*0.7)+0.1
    tmpVal = int(tmpVal * 256)
    tmpCol = [tmpVal,tmpVal,tmpVal,0]
    #Change the color of each material slightly
    if x % 3 == 0:
        if tmpCol[0] < 128: tmpCol[0] += 60
        else: tmpCol[0] -= 60
    if x % 3 == 1:
        if tmpCol[1] < 128: tmpCol[1] += 60
        else: tmpCol[1] -= 60
    if x % 3 == 2:
        if tmpCol[2] < 128: tmpCol[2] += 60
        else: tmpCol[2] -= 60
    #Add the material to the mesh
    VtxCol.append(tmpCol)

#================================================================================================== 
# Bone Weight
#================================================================================================== 
#read the RAWW0000 header
indata = unpack('20s3i',pskfile.read(32))
recCount = indata[3]
printlog( "Nbr of RAWW0000 records: " + str(recCount) +"\n")
#RAWW0000 fields: Weight|PntIdx|BoneIdx
RWghts = []
counter = 0
while counter < recCount:
    counter = counter + 1
    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()
printlog( "Vertex point and groups count =" + str(len(RWghts)) + "\n")
printlog("PntIdx|BoneIdx|Weight")
for vg in RWghts:
    printlog( str(vg[0]) + "|" + str(vg[1]) + "|" + str(vg[2]) + "\n")

#Tmsh.update_tag()

#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
        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
                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
#================================================================================================== 
print("vertex:",len(verts),"faces:",len(faces))
me_ob.vertices.add(len(verts))
me_ob.faces.add(len(faces)//4)

me_ob.vertices.foreach_set("co", unpack_list(verts))

me_ob.faces.foreach_set("vertices_raw", faces)
me_ob.faces.foreach_set("use_smooth", [False] * len(me_ob.faces))
me_ob.update_tag()

"""
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)
-fae 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)):
        #print((bpy.data.textures[texno].name))     
        #print(dir(bpy.data.textures[texno]))
    #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):
    #psktexname="psk" + str(countm)
    #me_ob.uv_textures.new(name=psktexname)
if importmultiuvtextures == True:
    me_ob.uv_textures.new(name="pskuvtexture")
    #print(dir(bpy.data))
    if (len(faceuv) > 0):
        for countm in range(len(me_ob.uv_textures)):
            me_ob.update()
            uvtex = me_ob.uv_textures[countm] #add one uv texture
            me_ob.update()
            #print("UV TEXTURE NAME:",uvtex.name)
            for i, face in enumerate(me_ob.faces):
                blender_tface = uvtex.data[i] #face
                mfaceuv = faceuv[i]
                #print("---------------------------------------")
                #print(faceuv[i][1])
                #print(dir(face))
                face.material_index = faceuv[i][1]
                blender_tface.uv1 = mfaceuv[0][0] #uv = (0,0)
                blender_tface.uv2 = mfaceuv[0][1] #uv = (0,0)
                blender_tface.uv3 = mfaceuv[0][2] #uv = (0,0)
            texture.append(uvtex)
else:
    for countm in range(materialcount):
        psktexname="psk" + str(countm)
        me_ob.uv_textures.new(name=psktexname)
        #psktexname="psk" + str(countm)
    #me_ob.uv_textures.new(name=psktexname)
    for countm in range(len(me_ob.uv_textures)):
        me_ob.update()
        #print(dir(me_ob.uv_textures))
        #psktexname="psk" + str(countm)
        uvtex = me_ob.uv_textures[countm] #add one uv texture
        me_ob.update()
        #print("UV TEXTURE NAME:",uvtex.name)
        if (len(faceuv) > 0):
            # counttex = 0  # UNUSED
            countm = 0
            for countm in range(len(me_ob.uv_textures)):
                me_ob.update()
                #print(dir(me_ob.uv_textures))
                psktexname="psk" + str(countm)
                uvtex = me_ob.uv_textures[countm] #add one uv texture
                me_ob.update()
                #print("UV TEXTURE NAME:",uvtex.name)
                for i, face in enumerate(me_ob.faces):
                    blender_tface = uvtex.data[i] #face
                    mfaceuv = faceuv[i]
                    #print("---------------------------------------")
                    #print(faceuv[i][1])
                    #print(dir(face))
                    face.material_index = faceuv[i][1]
                    if countm == faceuv[i][1]:
                        face.material_index = faceuv[i][1]
                        blender_tface.uv1 = mfaceuv[0][0] #uv = (0,0)
                        blender_tface.uv2 = mfaceuv[0][1] #uv = (0,0)
                        blender_tface.uv3 = mfaceuv[0][2] #uv = (0,0)
                    else:
                        #set uv to zero (0,0)
                        #print("--------------------")
                        #print(blender_tface.uv1)
                        #print(blender_tface.uv2)
                        #print(blender_tface.uv2)
                        blender_tface.uv1 = [0,0]
                        #print(blender_tface.uv1)
                        blender_tface.uv2 = [0,0]
                        blender_tface.uv3 = [0,0]

        texture.append(uvtex)
print("UV TEXTURE LEN:",len(texture))
    #for tex in me_ob.uv_textures:
        #print("mesh tex:",dir(tex))
        #print((tex.name))

#for face in me_ob.faces:
    #print(dir(face))


'''
matdata = bpy.data.materials.new(materialname)
#color is 0 - 1 not in 0 - 255
#matdata.mirror_color=(float(0.04),float(0.08),float(0.44))
matdata.diffuse_color=(float(0.04),float(0.08),float(0.44))#blue color
#print(dir(me_ob.uv_textures[0].data))
texdata = None
texdata = bpy.data.textures[len(bpy.data.textures)-1]
if (texdata != None):
    #print(texdata.name)
    #print(dir(texdata))
    texdata.name = "texturelist1"
    matdata.active_texture = texdata
materials.append(matdata)
#matdata = bpy.data.materials.new(materialname)
#materials.append(matdata)
#= make sure the list isnt too big
for material in materials:
    #add material to the mesh list of materials
    me_ob.materials.append(material)
'''
#===================================================================================================
#
#===================================================================================================
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:
    #print("names:",bone.name,":",dir(bone))
    #print("names:",bone.name)
    group = obmesh.vertex_groups.new(bone.name)
for vgroup in obmesh.vertex_groups:
    #print(vgroup.name,":",vgroup.index)
    for vgp in RWghts:
        #bone index
        if vgp[1] == vgroup.index:
            #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 ----")    
bpy.context.scene.objects.link(obmesh)    
bpy.context.scene.update()

print ("PSK2Blender completed")

End of def pskimport

def getInputFilename(self,filename,importmesh,importbone,bDebugLogPSK,importmultiuvtextures):
checktype = filename.split('')[-1].split('.')[1]
print ("------------",filename)
if checktype.lower() != 'psk':
print (" Selected file = ",filename)
raise (IOError, "The selected input file is not a *.psk file")
#self.report({'INFO'}, ("Selected file:"+ filename))
else:
pskimport(filename,importmesh,importbone,bDebugLogPSK,importmultiuvtextures)

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",
        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
    getInputFilename(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)
    return {'RUNNING_MODAL'}  

def menu_func(self, context):
self.layout.operator(IMPORT_OT_psk.bl_idname, text="Skeleton Mesh (.psk)")

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()

note this only read the data and will not be place in the scene

getInputFilename('C:\blenderfiles\BotA.psk')

getInputFilename('C:\blenderfiles\AA.PSK')

@flufy3d
Copy link
Author

flufy3d commented Sep 26, 2012

##### 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)",
"author": "Darknet",
"version": (2, 0),
"blender": (2, 5, 9),
"location": "File > Import > Skeleton Mesh (.psk)",
"description": "Import Skeleleton Mesh",
"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
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",
default=1,min=0.001,max=1000)

output log in to txt file

DEBUGLOG = False

scale = 1.0
bonesize = 1.0
md5_bones=[]

def unpack_list(list_of_tuples):
l = []
for t in list_of_tuples:
l.extend(t)
return l
"""
class md5_bone:
bone_index=0
name=""
bindpos=[]
bindmat = mathutils.Quaternion()
parent=""
parent_index=0
blenderbone=None
roll=0

def __init__(self):
    self.bone_index=0
    self.name=""
    self.bindpos=[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.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)

"""
class md5_bone:
bone_index=0
name=""
bindpos=[]
bindmat=[]
scale = []
parent=""
parent_index=0
blenderbone=None
roll=0

def __init__(self):
    self.bone_index=0
    self.name=""
    self.bindpos=[0.0]*3
    self.scale=[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.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)

http://www.blender.org/forum/viewtopic.php?t=13340&sid=8b17d5de07b17960021bbd72cac0495f

def fixRollZ(b):
v = (b.tail-b.head)/b.length
b.roll -= math.degrees(math.atan2(v[0]v[2](1 - v[1]),v[0]_v[0] + v[1]_v[2]_v[2]))
def fixRoll(b):
v = (b.tail-b.head)/b.length
if v[2]_v[2] > .5:
#align X-axis
b.roll += math.degrees(math.atan2(v[0]v[2](1 - v[1]),v[2]_v[2] + v[1]_v[0]_v[0]))
else:
#align Z-axis
b.roll -= math.degrees(math.atan2(v[0]v[2](1 - v[1]),v[0]_v[0] + v[1]_v[2]_v[2]))

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)

md5_bones=[]
pskfile = open(infile,'rb')
if (DEBUGLOG):
    logpath = infile.replace(".psk", ".txt")
    print("logpath:",logpath)
    logf = open(logpath,'w')

def printlog(strdata):
    if (DEBUGLOG):
        logf.write(strdata)

objName = infile.split('\\')[-1].split('.')[0]

me_ob = bpy.data.meshes.new(objName)
print("objName:",objName)
printlog(("New Mesh = " + me_ob.name + "\n"))
#read general header
indata = unpack('20s3i',pskfile.read(32))
#not using the general header at this time
#================================================================================================== 
# vertex point
#================================================================================================== 
#read the PNTS0000 header
indata = unpack('20s3i',pskfile.read(32))
recCount = indata[3]
printlog(( "Nbr of PNTS0000 records: " + str(recCount) + "\n"))
counter = 0
verts = []
while counter < recCount:
    counter = counter + 1
    indata = unpack('3f',pskfile.read(12))
    #print(indata[0],indata[1],indata[2])
    verts.extend([(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
indata = unpack('20s3i',pskfile.read(32))
recCount = indata[3]
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
    indata = unpack('hhffhh',pskfile.read(16))
    print(indata[4],end = " ")
    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
indata = unpack('20s3i',pskfile.read(32))
recCount = indata[3]
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 = []
#the psk values are: nWdgIdx1|WdgIdx2|WdgIdx3|MatIdx|AuxMatIdx|SmthGrp
printlog("nWdgIdx1|WdgIdx2|WdgIdx3|MatIdx|AuxMatIdx|SmthGrp \n")
while counter < recCount:
    counter = counter + 1
    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[0]][0]
    PNTSB = UVCoords[indata[1]][0]
    PNTSC = UVCoords[indata[2]][0]
    #print(PNTSA,PNTSB,PNTSC) #face id vertex
    #faces.extend([0,1,2,0])
    faces.extend([PNTSA,PNTSB,PNTSC,0])
    uv = []
    u0 = UVCoords[indata[0]][1]
    v0 = UVCoords[indata[0]][2]
    uv.append([u0,v0])
    u1 = UVCoords[indata[1]][1]
    v1 = UVCoords[indata[1]][2]
    uv.append([u1,v1])
    u2 = UVCoords[indata[2]][1]
    v2 = UVCoords[indata[2]][2]
    uv.append([u2,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
    ##Tmsh.faces[-1].uv = [(u0,v0),(u1,v1),(u2,v2)]
    #print("smooth:",indata[5])
    #collect a list of the smoothing groups
    if SGlist.count(indata[5]) == 0:
        SGlist.append(indata[5])
        print("smooth:",indata[5])
    #assign a material index to the face
    #Tmsh.faces[-1].materialIndex = SGlist.index(indata[5])
printlog( "Using Materials to represent PSK Smoothing Groups...\n")
#==========
# skip something...
#==========

#================================================================================================== 
# Material
#================================================================================================== 
##
#read the MATT0000 header
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
    indata = unpack('64s6i',pskfile.read(88))
    materialcount += 1
    print("Material",counter)
    print("Mat name %s",indata[0])

##

#================================================================================================== 
# Bones (Armature)
#================================================================================================== 
#read the REFSKEL0 header
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

Bns = []
bone = []
#================================================================================================== 
# 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:
    indata = unpack('64s3i11f',pskfile.read(120))
    #print( "DATA",str(indata))

    bone.append(indata)

    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'))
    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")
    print ("temp_name:", temp_name, "||")
    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]

    #w,x,y,z
    if (counter == 0):#main parent
        print("no parent bone")
        createbone.bindmat = mathutils.Quaternion((indata[7],-indata[4],-indata[5],-indata[6])).normalized().to_matrix()

    else:
        print("parent bone")
        createbone.bindmat = mathutils.Quaternion((indata[7],indata[4],indata[5],indata[6])).normalized().to_matrix()


    md5_bones.append(createbone)
    counter = counter + 1
    bnstr = (str(indata[0]))
    Bns.append(bnstr)

for pbone in md5_bones:
    pbone.parent =  md5_bones[pbone.parent_index]

for pbone in md5_bones:
    if pbone.name != pbone.parent:
        pbone.bindmat = pbone.bindmat * pbone.parent.bindmat
    else:
        pbone.bindmat = pbone.bindmat

for pbone in md5_bones:
    pbone.parent =  md5_bones[pbone.parent_index].name



bonecount = 0
for armbone in bone:
    temp_name = armbone[0][:30]
    #print ("BONE NAME: ",len(temp_name))
    temp_name=str((temp_name))
    #temp_name = temp_name[1]
    #print ("BONE NAME: ",temp_name)
    bonecount +=1
print ("-------------------------")
print ("----Creating--Armature---")
print ("-------------------------")

#================================================================================================
#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')
        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
        bpy.ops.object.mode_set(mode='EDIT')

        #newbone = ob_new.data.edit_bones.new('test')
        #newbone.tail.y = 1
        print("creating bone(s)")
        for bone in md5_bones:
            #print(dir(bone))
            bpy.ops.object.mode_set(mode='EDIT')
            newbone = ob_new.data.edit_bones.new(bone.name)
        '''     


        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')
        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)")
        for bone in md5_bones:
            #print(dir(bone))
            newbone = ob_new.data.edit_bones.new(bone.name)
            #parent the bone
            #print("DRI:",dir(newbone))
            parentbone = None
            print("bone name:",bone.name)
            #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]

                #print( "LINKING:" , bone.parent ,"j")
                parentbone = ob_new.data.edit_bones[bone.parent]
                newbone.parent = parentbone

                rotmatrix = bone.bindmat



                #parentbone.head.x
                newbone.head.x = pos_x
                newbone.head.y = pos_y
                newbone.head.z = pos_z
                #print("head:",newbone.head)
                newbone.tail.x = (pos_x + bonesize * rotmatrix[0][1])
                newbone.tail.y = (pos_y + bonesize * rotmatrix[1][1])
                newbone.tail.z = (pos_z + bonesize * rotmatrix[2][1])
                #newbone.roll = fixRoll(newbone)
                newbone.transform(bone.bindmat,True,True)
                newbone.translate(parentbone.head)





            else:

                rotmatrix = bone.bindmat
                newbone.transform(bone.bindmat,True,True)
                newbone.head.x = bone.bindpos[0]
                newbone.head.y = bone.bindpos[1]
                newbone.head.z = bone.bindpos[2]
                newbone.tail.x = bone.bindpos[0] + bonesize * rotmatrix[0][1]
                newbone.tail.y = bone.bindpos[1] + bonesize * rotmatrix[1][1]
                newbone.tail.z = bone.bindpos[2] + bonesize * rotmatrix[2][1]
                #newbone.roll = fixRoll(newbone)
                #print("no parent")



bpy.context.scene.update()

return

#==================================================================================================
#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
    tmpVal = ((float(x)+1.0)/(len(Bns))*0.7)+0.1
    tmpVal = int(tmpVal * 256)
    tmpCol = [tmpVal,tmpVal,tmpVal,0]
    #Change the color of each material slightly
    if x % 3 == 0:
        if tmpCol[0] < 128: tmpCol[0] += 60
        else: tmpCol[0] -= 60
    if x % 3 == 1:
        if tmpCol[1] < 128: tmpCol[1] += 60
        else: tmpCol[1] -= 60
    if x % 3 == 2:
        if tmpCol[2] < 128: tmpCol[2] += 60
        else: tmpCol[2] -= 60
    #Add the material to the mesh
    VtxCol.append(tmpCol)

#================================================================================================== 
# Bone Weight
#================================================================================================== 
#read the RAWW0000 header
indata = unpack('20s3i',pskfile.read(32))
recCount = indata[3]
printlog( "Nbr of RAWW0000 records: " + str(recCount) +"\n")
#RAWW0000 fields: Weight|PntIdx|BoneIdx
RWghts = []
counter = 0
while counter < recCount:
    counter = counter + 1
    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()
printlog( "Vertex point and groups count =" + str(len(RWghts)) + "\n")
printlog("PntIdx|BoneIdx|Weight")
for vg in RWghts:
    printlog( str(vg[0]) + "|" + str(vg[1]) + "|" + str(vg[2]) + "\n")

#Tmsh.update_tag()

#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
        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
                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
#================================================================================================== 
print("vertex:",len(verts),"faces:",len(faces))
me_ob.vertices.add(len(verts))
me_ob.faces.add(len(faces)//4)

me_ob.vertices.foreach_set("co", unpack_list(verts))

me_ob.faces.foreach_set("vertices_raw", faces)
me_ob.faces.foreach_set("use_smooth", [False] * len(me_ob.faces))
me_ob.update_tag()

"""
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)
-fae 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)):
        #print((bpy.data.textures[texno].name))     
        #print(dir(bpy.data.textures[texno]))
    #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):
    #psktexname="psk" + str(countm)
    #me_ob.uv_textures.new(name=psktexname)
if importmultiuvtextures == True:
    me_ob.uv_textures.new(name="pskuvtexture")
    #print(dir(bpy.data))
    if (len(faceuv) > 0):
        for countm in range(len(me_ob.uv_textures)):
            me_ob.update()
            uvtex = me_ob.uv_textures[countm] #add one uv texture
            me_ob.update()
            #print("UV TEXTURE NAME:",uvtex.name)
            for i, face in enumerate(me_ob.faces):
                blender_tface = uvtex.data[i] #face
                mfaceuv = faceuv[i]
                #print("---------------------------------------")
                #print(faceuv[i][1])
                #print(dir(face))
                face.material_index = faceuv[i][1]
                blender_tface.uv1 = mfaceuv[0][0] #uv = (0,0)
                blender_tface.uv2 = mfaceuv[0][1] #uv = (0,0)
                blender_tface.uv3 = mfaceuv[0][2] #uv = (0,0)
            texture.append(uvtex)
else:
    for countm in range(materialcount):
        psktexname="psk" + str(countm)
        me_ob.uv_textures.new(name=psktexname)
        #psktexname="psk" + str(countm)
    #me_ob.uv_textures.new(name=psktexname)
    for countm in range(len(me_ob.uv_textures)):
        me_ob.update()
        #print(dir(me_ob.uv_textures))
        #psktexname="psk" + str(countm)
        uvtex = me_ob.uv_textures[countm] #add one uv texture
        me_ob.update()
        #print("UV TEXTURE NAME:",uvtex.name)
        if (len(faceuv) > 0):
            # counttex = 0  # UNUSED
            countm = 0
            for countm in range(len(me_ob.uv_textures)):
                me_ob.update()
                #print(dir(me_ob.uv_textures))
                psktexname="psk" + str(countm)
                uvtex = me_ob.uv_textures[countm] #add one uv texture
                me_ob.update()
                #print("UV TEXTURE NAME:",uvtex.name)
                for i, face in enumerate(me_ob.faces):
                    blender_tface = uvtex.data[i] #face
                    mfaceuv = faceuv[i]
                    #print("---------------------------------------")
                    #print(faceuv[i][1])
                    #print(dir(face))
                    face.material_index = faceuv[i][1]
                    if countm == faceuv[i][1]:
                        face.material_index = faceuv[i][1]
                        blender_tface.uv1 = mfaceuv[0][0] #uv = (0,0)
                        blender_tface.uv2 = mfaceuv[0][1] #uv = (0,0)
                        blender_tface.uv3 = mfaceuv[0][2] #uv = (0,0)
                    else:
                        #set uv to zero (0,0)
                        #print("--------------------")
                        #print(blender_tface.uv1)
                        #print(blender_tface.uv2)
                        #print(blender_tface.uv2)
                        blender_tface.uv1 = [0,0]
                        #print(blender_tface.uv1)
                        blender_tface.uv2 = [0,0]
                        blender_tface.uv3 = [0,0]

        texture.append(uvtex)
print("UV TEXTURE LEN:",len(texture))
    #for tex in me_ob.uv_textures:
        #print("mesh tex:",dir(tex))
        #print((tex.name))

#for face in me_ob.faces:
    #print(dir(face))


'''
matdata = bpy.data.materials.new(materialname)
#color is 0 - 1 not in 0 - 255
#matdata.mirror_color=(float(0.04),float(0.08),float(0.44))
matdata.diffuse_color=(float(0.04),float(0.08),float(0.44))#blue color
#print(dir(me_ob.uv_textures[0].data))
texdata = None
texdata = bpy.data.textures[len(bpy.data.textures)-1]
if (texdata != None):
    #print(texdata.name)
    #print(dir(texdata))
    texdata.name = "texturelist1"
    matdata.active_texture = texdata
materials.append(matdata)
#matdata = bpy.data.materials.new(materialname)
#materials.append(matdata)
#= make sure the list isnt too big
for material in materials:
    #add material to the mesh list of materials
    me_ob.materials.append(material)
'''
#===================================================================================================
#
#===================================================================================================
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:
    #print("names:",bone.name,":",dir(bone))
    #print("names:",bone.name)
    group = obmesh.vertex_groups.new(bone.name)
for vgroup in obmesh.vertex_groups:
    #print(vgroup.name,":",vgroup.index)
    for vgp in RWghts:
        #bone index
        if vgp[1] == vgroup.index:
            #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 ----")    
bpy.context.scene.objects.link(obmesh)    
bpy.context.scene.update()

print ("PSK2Blender completed")

End of def pskimport

def getInputFilename(self,filename,importmesh,importbone,bDebugLogPSK,importmultiuvtextures):
checktype = filename.split('')[-1].split('.')[1]
print ("------------",filename)
if checktype.lower() != 'psk':
print (" Selected file = ",filename)
raise (IOError, "The selected input file is not a *.psk file")
#self.report({'INFO'}, ("Selected file:"+ filename))
else:
pskimport(filename,importmesh,importbone,bDebugLogPSK,importmultiuvtextures)

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",
        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
    getInputFilename(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)
    return {'RUNNING_MODAL'}  

def menu_func(self, context):
self.layout.operator(IMPORT_OT_psk.bl_idname, text="Skeleton Mesh (.psk)")

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()

note this only read the data and will not be place in the scene

getInputFilename('C:\blenderfiles\BotA.psk')

getInputFilename('C:\blenderfiles\AA.PSK')

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment