Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCoDEmanX <codemanx@gmx.de>2014-02-04 06:05:05 +0400
committerCoDEmanX <codemanx@gmx.de>2014-02-04 06:05:05 +0400
commit049a5a86edcf78b070b73b141b657825145ae9a1 (patch)
tree6e5edf67d7974e0ad8beb209c57fd48c456ecfbe /io_export_unreal_psk_psa.py
parentd9fcbd06018165cc2cfca0673c7c2db44b04a833 (diff)
Clean-up: Updated bl_info['tracker_url'] to developer.blender.org, some minor other edits
Diffstat (limited to 'io_export_unreal_psk_psa.py')
-rw-r--r--io_export_unreal_psk_psa.py697
1 files changed, 348 insertions, 349 deletions
diff --git a/io_export_unreal_psk_psa.py b/io_export_unreal_psk_psa.py
index 787ca24d..d2982b34 100644
--- a/io_export_unreal_psk_psa.py
+++ b/io_export_unreal_psk_psa.py
@@ -24,14 +24,13 @@ bl_info = {
"location": "File > Export > Skeletal Mesh/Animation Data (.psk/.psa)",
"description": "Export Skeleletal Mesh/Animation Data",
"warning": "",
- "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"\
+ "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
"Scripts/Import-Export/Unreal_psk_psa",
- "tracker_url": "https://projects.blender.org/tracker/index.php?"\
- "func=detail&aid=21366",
+ "tracker_url": "https://developer.blender.org/T21366",
"category": "Import-Export"}
"""
--- Unreal Skeletal Mesh and Animation Export (.psk and .psa) export script v0.0.1 --<br>
+-- Unreal Skeletal Mesh and Animation Export (.psk and .psa) export script v0.0.1 --<br>
- NOTES:
- This script Exports To Unreal's PSK and PSA file formats for Skeletal Meshes and Animations. <br>
@@ -45,11 +44,11 @@ bl_info = {
[ - Edit by: Darknet
- v0.0.3 - v0.0.12
-- This will work on UT3 and it is a stable version that work with vehicle for testing.
+- This will work on UT3 and it is a stable version that work with vehicle for testing.
- Main Bone fix no dummy needed to be there.
- Just bone issues position, rotation, and offset for psk.
- The armature bone position, rotation, and the offset of the bone is fix. It was to deal with skeleton mesh export for psk.
-- Animation is fix for position, offset, rotation bone support one rotation direction when armature build.
+- Animation is fix for position, offset, rotation bone support one rotation direction when armature build.
- It will convert your mesh into triangular when exporting to psk file.
- Did not work with psa export yet.
@@ -151,7 +150,7 @@ from bpy.props import *
from struct import pack
# REFERENCE MATERIAL JUST IN CASE:
-#
+#
# U = x / sqrt(x^2 + y^2 + z^2)
# V = y / sqrt(x^2 + y^2 + z^2)
#
@@ -196,21 +195,21 @@ def verbose( msg, iteration=-1, max_iterations=4, msg_truncated="..." ):
return
print(msg)
-
+
#===========================================================================
# Log header/separator
#===========================================================================
def header( msg, justify='LEFT', spacer='_', cols=78 ):
-
+
if justify == 'LEFT':
s = '{:{spacer}<{cols}}'.format(msg+" ", spacer=spacer, cols=cols)
-
+
elif justify == 'RIGHT':
s = '{:{spacer}>{cols}}'.format(" "+msg, spacer=spacer, cols=cols)
-
+
else:
s = '{:{spacer}^{cols}}'.format(" "+msg+" ", spacer=spacer, cols=cols)
-
+
return "\n" + s + "\n"
#===========================================================================
@@ -218,11 +217,11 @@ def header( msg, justify='LEFT', spacer='_', cols=78 ):
# the object must be usable as a dictionary key
#===========================================================================
class ObjMap:
-
+
def __init__(self):
self.dict = {}
self.next = 0
-
+
def get(self, obj):
if obj in self.dict:
return self.dict[obj]
@@ -231,37 +230,37 @@ class ObjMap:
self.next = self.next + 1
self.dict[obj] = id
return id
-
+
def items(self):
getval = operator.itemgetter(0)
getkey = operator.itemgetter(1)
return map(getval, sorted(self.dict.items(), key=getkey))
#===========================================================================
-# RG - UNREAL DATA STRUCTS - CONVERTED FROM C STRUCTS GIVEN ON UDN SITE
+# RG - UNREAL DATA STRUCTS - CONVERTED FROM C STRUCTS GIVEN ON UDN SITE
# provided here: http://udn.epicgames.com/Two/BinaryFormatSpecifications.html
# updated UDK (Unreal Engine 3): http://udn.epicgames.com/Three/BinaryFormatSpecifications.html
#===========================================================================
class FQuat:
- def __init__(self):
+ def __init__(self):
self.X = 0.0
self.Y = 0.0
self.Z = 0.0
self.W = 1.0
-
+
def dump(self):
return pack('ffff', self.X, self.Y, self.Z, self.W)
-
+
def __cmp__(self, other):
return cmp(self.X, other.X) \
or cmp(self.Y, other.Y) \
or cmp(self.Z, other.Z) \
or cmp(self.W, other.W)
-
+
def __hash__(self):
return hash(self.X) ^ hash(self.Y) ^ hash(self.Z) ^ hash(self.W)
-
+
def __str__(self):
return "[%f,%f,%f,%f](FQuat)" % (self.X, self.Y, self.Z, self.W)
@@ -271,34 +270,34 @@ class FVector(object):
self.X = X
self.Y = Y
self.Z = Z
-
+
def dump(self):
return pack('fff', self.X, self.Y, self.Z)
-
+
def __cmp__(self, other):
return cmp(self.X, other.X) \
or cmp(self.Y, other.Y) \
or cmp(self.Z, other.Z)
-
+
def _key(self):
return (type(self).__name__, self.X, self.Y, self.Z)
-
+
def __hash__(self):
return hash(self._key())
-
+
def __eq__(self, other):
if not hasattr(other, '_key'):
return False
- return self._key() == other._key()
-
+ return self._key() == other._key()
+
def dot(self, other):
return self.X * other.X + self.Y * other.Y + self.Z * other.Z
-
+
def cross(self, other):
return FVector(self.Y * other.Z - self.Z * other.Y,
self.Z * other.X - self.X * other.Z,
self.X * other.Y - self.Y * other.X)
-
+
def sub(self, other):
return FVector(self.X - other.X,
self.Y - other.Y,
@@ -313,7 +312,7 @@ class VJointPos:
self.XSize = 0.0
self.YSize = 0.0
self.ZSize = 0.0
-
+
def dump(self):
return self.Orientation.dump() + self.Position.dump() + pack('4f', self.Length, self.XSize, self.YSize, self.ZSize)
@@ -332,7 +331,7 @@ class AnimInfoBinary:
self.StartBone = 0
self.FirstRawFrame = 0
self.NumRawFrames = 0
-
+
def dump(self):
return pack('64s64siiiifffiii', str.encode(self.Name), str.encode(self.Group), self.TotalBones, self.RootInclude, self.KeyCompressionStyle, self.KeyQuotum, self.KeyPrediction, self.TrackTime, self.AnimRate, self.StartBone, self.FirstRawFrame, self.NumRawFrames)
@@ -343,7 +342,7 @@ class VChunkHeader:
self.TypeFlag = 1999801 # special value
self.DataSize = type_size
self.DataCount = 0
-
+
def dump(self):
return pack('20siii', self.ChunkID, self.TypeFlag, self.DataSize, self.DataCount)
@@ -357,7 +356,7 @@ class VMaterial:
self.AuxFlags = 0 # DWORD
self.LodBias = 0
self.LodStyle = 0
-
+
def dump(self):
#print("DATA MATERIAL:",self.MaterialName)
return pack('64siLiLii', str.encode(self.MaterialName), self.TextureIndex, self.PolyFlags, self.AuxMaterial, self.AuxFlags, self.LodBias, self.LodStyle)
@@ -370,11 +369,11 @@ class VBone:
self.NumChildren = 0
self.ParentIndex = 0
self.BonePos = VJointPos()
-
+
def dump(self):
return pack('64sLii', str.encode(self.Name), self.Flags, self.NumChildren, self.ParentIndex) + self.BonePos.dump()
-#same as above - whatever - this is how Epic does it...
+#same as above - whatever - this is how Epic does it...
class FNamedBoneBinary:
def __init__(self):
@@ -384,7 +383,7 @@ class FNamedBoneBinary:
self.ParentIndex = 0
self.BonePos = VJointPos()
self.IsRealBone = 0 # this is set to 1 when the bone is actually a bone in the mesh and not a dummy
-
+
def dump(self):
return pack('64sLii', str.encode(self.Name), self.Flags, self.NumChildren, self.ParentIndex) + self.BonePos.dump()
@@ -394,7 +393,7 @@ class VRawBoneInfluence:
self.Weight = 0.0
self.PointIndex = 0
self.BoneIndex = 0
-
+
def dump(self):
return pack('fii', self.Weight, self.PointIndex, self.BoneIndex)
@@ -404,7 +403,7 @@ class VQuatAnimKey:
self.Position = FVector()
self.Orientation = FQuat()
self.Time = 0.0
-
+
def dump(self):
return self.Position.dump() + self.Orientation.dump() + pack('f', self.Time)
@@ -416,25 +415,25 @@ class VVertex(object):
self.V = 0.0
self.MatIndex = 0 # BYTE
self.Reserved = 0 # BYTE
- self.SmoothGroup = 0
-
+ self.SmoothGroup = 0
+
def dump(self):
return pack('HHffBBH', self.PointIndex, 0, self.U, self.V, self.MatIndex, self.Reserved, 0)
-
+
def __cmp__(self, other):
return cmp(self.PointIndex, other.PointIndex) \
or cmp(self.U, other.U) \
or cmp(self.V, other.V) \
or cmp(self.MatIndex, other.MatIndex) \
or cmp(self.Reserved, other.Reserved) \
- or cmp(self.SmoothGroup, other.SmoothGroup )
-
+ or cmp(self.SmoothGroup, other.SmoothGroup )
+
def _key(self):
return (type(self).__name__, self.PointIndex, self.U, self.V, self.MatIndex, self.Reserved)
-
+
def __hash__(self):
return hash(self._key())
-
+
def __eq__(self, other):
if not hasattr(other, '_key'):
return False
@@ -447,7 +446,7 @@ class VPointSimple:
def __cmp__(self, other):
return cmp(self.Point, other.Point)
-
+
def __hash__(self):
return hash(self._key())
@@ -463,26 +462,26 @@ class VPoint(object):
def __init__(self):
self.Point = FVector()
- self.SmoothGroup = 0
-
+ self.SmoothGroup = 0
+
def dump(self):
return self.Point.dump()
-
+
def __cmp__(self, other):
return cmp(self.Point, other.Point) \
- or cmp(self.SmoothGroup, other.SmoothGroup)
-
+ or cmp(self.SmoothGroup, other.SmoothGroup)
+
def _key(self):
return (type(self).__name__, self.Point, self.SmoothGroup)
-
+
def __hash__(self):
return hash(self._key()) \
- ^ hash(self.SmoothGroup)
-
+ ^ hash(self.SmoothGroup)
+
def __eq__(self, other):
if not hasattr(other, '_key'):
return False
- return self._key() == other._key()
+ return self._key() == other._key()
class VTriangle:
@@ -493,7 +492,7 @@ class VTriangle:
self.MatIndex = 0 # BYTE
self.AuxMatIndex = 0 # BYTE
self.SmoothingGroups = 0 # DWORD
-
+
def dump(self):
return pack('HHHBBL', self.WedgeIndex0, self.WedgeIndex1, self.WedgeIndex2, self.MatIndex, self.AuxMatIndex, self.SmoothingGroups)
#print("smooth",self.SmoothingGroups)
@@ -503,21 +502,21 @@ class VTriangle:
#===========================================================================
#===========================================================================
-# RG - helper class to handle the normal way the UT files are stored
+# RG - helper class to handle the normal way the UT files are stored
# as sections consisting of a header and then a list of data structures
#===========================================================================
class FileSection:
-
+
def __init__(self, name, type_size):
self.Header = VChunkHeader(name, type_size)
self.Data = [] # list of datatypes
-
+
def dump(self):
data = self.Header.dump()
for i in range(len(self.Data)):
data = data + self.Data[i].dump()
return data
-
+
def UpdateHeader(self):
self.Header.DataCount = len(self.Data)
@@ -525,7 +524,7 @@ class FileSection:
# PSK
#===========================================================================
class PSKFile:
-
+
def __init__(self):
self.GeneralHeader = VChunkHeader("ACTRHEAD", 0)
self.Points = FileSection("PNTS0000", SIZE_VPOINT) # VPoint
@@ -534,8 +533,8 @@ class PSKFile:
self.Materials = FileSection("MATT0000", SIZE_VMATERIAL) # VMaterial
self.Bones = FileSection("REFSKELT", SIZE_VBONE) # VBone
self.Influences = FileSection("RAWWEIGHTS", SIZE_VRAWBONEINFLUENCE) # VRawBoneInfluence
-
- #RG - this mapping is not dumped, but is used internally to store the new point indices
+
+ #RG - this mapping is not dumped, but is used internally to store the new point indices
# for vertex groups calculated during the mesh dump, so they can be used again
# to dump bone influences during the armature dump
#
@@ -545,29 +544,29 @@ class PSKFile:
# Layout:
# { groupname : [ (index, weight), ... ], ... }
#
- # example:
+ # example:
# { 'MyVertexGroup' : [ (0, 1.0), (5, 1.0), (3, 0.5) ] , 'OtherGroup' : [(2, 1.0)] }
-
- self.VertexGroups = {}
-
+
+ self.VertexGroups = {}
+
def AddPoint(self, p):
self.Points.Data.append(p)
-
+
def AddWedge(self, w):
self.Wedges.Data.append(w)
-
+
def AddFace(self, f):
self.Faces.Data.append(f)
-
+
def AddMaterial(self, m):
self.Materials.Data.append(m)
-
+
def AddBone(self, b):
self.Bones.Data.append(b)
-
+
def AddInfluence(self, i):
self.Influences.Data.append(i)
-
+
def UpdateHeaders(self):
self.Points.UpdateHeader()
self.Wedges.UpdateHeader()
@@ -575,12 +574,12 @@ class PSKFile:
self.Materials.UpdateHeader()
self.Bones.UpdateHeader()
self.Influences.UpdateHeader()
-
+
def dump(self):
self.UpdateHeaders()
data = self.GeneralHeader.dump() + self.Points.dump() + self.Wedges.dump() + self.Faces.dump() + self.Materials.dump() + self.Bones.dump() + self.Influences.dump()
return data
-
+
def GetMatByIndex(self, mat_index):
if mat_index >= 0 and len(self.Materials.Data) > mat_index:
return self.Materials.Data[mat_index]
@@ -590,7 +589,7 @@ class PSKFile:
m.MaterialName = MaterialName[mat_index]
self.AddMaterial(m)
return m
-
+
def PrintOut(self):
print( "{:>16} {:}".format( "Points", len(self.Points.Data) ) )
print( "{:>16} {:}".format( "Wedges", len(self.Wedges.Data) ) )
@@ -603,16 +602,16 @@ class PSKFile:
# PSA
#
# Notes from UDN:
-# The raw key array holds all the keys for all the bones in all the specified sequences,
+# The raw key array holds all the keys for all the bones in all the specified sequences,
# organized as follows:
-# For each AnimInfoBinary's sequence there are [Number of bones] times [Number of frames keys]
-# in the VQuatAnimKeys, laid out as tracks of [numframes] keys for each bone in the order of
-# the bones as defined in the array of FnamedBoneBinary in the PSA.
+# For each AnimInfoBinary's sequence there are [Number of bones] times [Number of frames keys]
+# in the VQuatAnimKeys, laid out as tracks of [numframes] keys for each bone in the order of
+# the bones as defined in the array of FnamedBoneBinary in the PSA.
#
-# Once the data from the PSK (now digested into native skeletal mesh) and PSA (digested into
-# a native animation object containing one or more sequences) are associated together at runtime,
-# bones are linked up by name. Any bone in a skeleton (from the PSK) that finds no partner in
-# the animation sequence (from the PSA) will assume its reference pose stance ( as defined in
+# Once the data from the PSK (now digested into native skeletal mesh) and PSA (digested into
+# a native animation object containing one or more sequences) are associated together at runtime,
+# bones are linked up by name. Any bone in a skeleton (from the PSK) that finds no partner in
+# the animation sequence (from the PSA) will assume its reference pose stance ( as defined in
# the offsets & rotations that are in the VBones making up the reference skeleton from the PSK)
#===========================================================================
class PSAFile:
@@ -624,57 +623,57 @@ class PSAFile:
self.RawKeys = FileSection("ANIMKEYS", SIZE_VQUATANIMKEY) #VQuatAnimKey
# this will take the format of key=Bone Name, value = (BoneIndex, Bone Object)
# THIS IS NOT DUMPED
- self.BoneLookup = {}
+ self.BoneLookup = {}
def AddBone(self, b):
self.Bones.Data.append(b)
-
+
def AddAnimation(self, a):
self.Animations.Data.append(a)
-
+
def AddRawKey(self, k):
self.RawKeys.Data.append(k)
-
+
def UpdateHeaders(self):
self.Bones.UpdateHeader()
self.Animations.UpdateHeader()
self.RawKeys.UpdateHeader()
-
+
def GetBoneByIndex(self, bone_index):
if bone_index >= 0 and len(self.Bones.Data) > bone_index:
return self.Bones.Data[bone_index]
-
+
def IsEmpty(self):
return (len(self.Bones.Data) == 0 or len(self.Animations.Data) == 0)
-
+
def StoreBone(self, b):
self.BoneLookup[b.Name] = [-1, b]
-
+
def UseBone(self, bone_name):
if bone_name in self.BoneLookup:
bone_data = self.BoneLookup[bone_name]
-
+
if bone_data[0] == -1:
bone_data[0] = len(self.Bones.Data)
self.AddBone(bone_data[1])
#self.Bones.Data.append(bone_data[1])
-
+
return bone_data[0]
-
+
def GetBoneByName(self, bone_name):
if bone_name in self.BoneLookup:
bone_data = self.BoneLookup[bone_name]
return bone_data[1]
-
+
def GetBoneIndex(self, bone_name):
if bone_name in self.BoneLookup:
bone_data = self.BoneLookup[bone_name]
return bone_data[0]
-
+
def dump(self):
self.UpdateHeaders()
return self.GeneralHeader.dump() + self.Bones.dump() + self.Animations.dump() + self.RawKeys.dump()
-
+
def PrintOut(self):
print( "{:>16} {:}".format( "Bones", len(self.Bones.Data) ) )
print( "{:>16} {:}".format( "Animations", len(self.Animations.Data) ) )
@@ -709,7 +708,7 @@ def make_namedbonebinary( name, parent_index, child_count, orientation_quat, pos
bone.BonePos.Position.Y = position_vect.y
bone.BonePos.Position.Z = position_vect.z
bone.IsRealBone = is_real
- return bone
+ return bone
def make_fquat( bquat ):
quat = FQuat()
@@ -719,7 +718,7 @@ def make_fquat( bquat ):
quat.Z = -bquat.z
quat.W = bquat.w
return quat
-
+
def make_fquat_default( bquat ):
quat = FQuat()
#print(dir(bquat))
@@ -737,7 +736,7 @@ def is_1d_face( face, mesh ):
v0 = face.vertices[0]
v1 = face.vertices[1]
v2 = face.vertices[2]
-
+
return (mesh.vertices[v0].co == mesh.vertices[v1].co \
or mesh.vertices[v1].co == mesh.vertices[v2].co \
or mesh.vertices[v2].co == mesh.vertices[v0].co)
@@ -748,9 +747,9 @@ def is_1d_face( face, mesh ):
# (renamed to seperate it from VVertex.SmoothGroup)
#===========================================================================
class SmoothingGroup:
-
+
static_id = 1
-
+
def __init__(self):
self.faces = []
self.neighboring_faces = []
@@ -758,12 +757,12 @@ class SmoothingGroup:
self.id = -1
self.local_id = SmoothingGroup.static_id
SmoothingGroup.static_id += 1
-
+
def __cmp__(self, other):
if isinstance(other, SmoothingGroup):
return cmp( self.local_id, other.local_id )
return -1
-
+
def __hash__(self):
return hash(self.local_id)
@@ -776,37 +775,37 @@ class SmoothingGroup:
temp_id = temp_id << 1
else:
raise Error("Smoothing Group ID Overflowed, Smoothing Group evidently has more than 31 neighboring groups")
-
+
self.id = temp_id
return self.id
-
+
def make_neighbor(self, new_neighbor):
if new_neighbor not in self.neighboring_groups:
self.neighboring_groups.append( new_neighbor )
def contains_face(self, face):
return (face in self.faces)
-
+
def add_neighbor_face(self, face):
if not face in self.neighboring_faces:
self.neighboring_faces.append( face )
-
+
def add_face(self, face):
if not face in self.faces:
self.faces.append( face )
def determine_edge_sharing( mesh ):
-
+
edge_sharing_list = dict()
-
+
for edge in mesh.edges:
edge_sharing_list[edge.key] = []
-
+
for face in mesh.tessfaces:
for key in face.edge_keys:
if not face in edge_sharing_list[key]:
edge_sharing_list[key].append(face) # mark this face as sharing this edge
-
+
return edge_sharing_list
def find_edges( mesh, key ):
@@ -819,21 +818,21 @@ def find_edges( mesh, key ):
return edge.index
def add_face_to_smoothgroup( mesh, face, edge_sharing_list, smoothgroup ):
-
+
if face in smoothgroup.faces:
return
smoothgroup.add_face(face)
-
+
for key in face.edge_keys:
-
+
edge_id = find_edges(mesh, key)
-
+
if edge_id != None:
-
+
# not sharp
if not( mesh.edges[edge_id].use_edge_sharp):
-
+
for shared_face in edge_sharing_list[key]:
if shared_face != face:
# recursive
@@ -845,14 +844,14 @@ def add_face_to_smoothgroup( mesh, face, edge_sharing_list, smoothgroup ):
smoothgroup.add_neighbor_face( shared_face )
def determine_smoothgroup_for_face( mesh, face, edge_sharing_list, smoothgroup_list ):
-
+
for group in smoothgroup_list:
if (face in group.faces):
return
-
+
smoothgroup = SmoothingGroup();
add_face_to_smoothgroup( mesh, face, edge_sharing_list, smoothgroup )
-
+
if not smoothgroup in smoothgroup_list:
smoothgroup_list.append( smoothgroup )
@@ -869,16 +868,16 @@ def build_neighbors_tree( smoothgroup_list ):
# parse_smooth_groups
#===========================================================================
def parse_smooth_groups( mesh ):
-
+
print("Parsing smooth groups...")
-
+
t = time.clock()
smoothgroup_list = []
edge_sharing_list = determine_edge_sharing(mesh)
#print("faces:",len(mesh.tessfaces))
interval = math.floor(len(mesh.tessfaces) / 100)
if interval == 0: #if the faces are few do this
- interval = math.floor(len(mesh.tessfaces) / 10)
+ interval = math.floor(len(mesh.tessfaces) / 10)
#print("FACES:",len(mesh.tessfaces),"//100 =" "interval:",interval)
for face in mesh.tessfaces:
#print(dir(face))
@@ -888,14 +887,14 @@ def parse_smooth_groups( mesh ):
print("Processing... {}%\r".format( int(face.index / len(mesh.tessfaces) * 100) ), end='')
sys.stdout.flush()
print("Completed" , ' '*20)
-
+
verbose("len(smoothgroup_list)={}".format(len(smoothgroup_list)))
-
+
build_neighbors_tree(smoothgroup_list)
-
+
for group in smoothgroup_list:
group.get_valid_smoothgroup_id()
-
+
print("Smooth group parsing completed in {:.2f}s".format(time.clock() - t))
return smoothgroup_list
@@ -904,11 +903,11 @@ def parse_smooth_groups( mesh ):
# blender 2.50 format using the Operators/command convert the mesh to tri mesh
#===========================================================================
def triangulate_mesh( object ):
-
+
verbose(header("triangulateNMesh"))
#print(type(object))
scene = bpy.context.scene
-
+
me_ob = object.copy()
me_ob.data = object.to_mesh(bpy.context.scene, True, 'PREVIEW') #write data object
bpy.context.scene.objects.link(me_ob)
@@ -916,24 +915,24 @@ def triangulate_mesh( object ):
bpy.ops.object.mode_set(mode='OBJECT')
for i in scene.objects:
i.select = False # deselect all objects
-
+
me_ob.select = True
scene.objects.active = me_ob
-
+
print("Copy and Convert mesh just incase any way...")
-
+
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')# select all the face/vertex/edge
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.quads_convert_to_tris()
bpy.context.scene.update()
-
+
bpy.ops.object.mode_set(mode='OBJECT')
-
+
bpy.context.scene.udk_option_triangulate = True
-
+
verbose("Triangulated mesh")
-
+
me_ob.data = me_ob.to_mesh(bpy.context.scene, True, 'PREVIEW') #write data object
bpy.context.scene.update()
return me_ob
@@ -945,7 +944,7 @@ def meshmerge(selectedobjects):
if len(selectedobjects) > 1:
print("selectedobjects:",len(selectedobjects)) #print select object
count = 0 #reset count
- for count in range(len( selectedobjects)):
+ for count in range(len( selectedobjects)):
#print("Index:",count)
if selectedobjects[count] != None:
me_da = selectedobjects[count].data.copy() #copy data
@@ -967,7 +966,7 @@ def meshmerge(selectedobjects):
if len(cloneobjects) > 1:
bpy.types.Scene.udk_copy_merge = True
return cloneobjects[0]
-
+
#sort the mesh center top list and not center at the last array. Base on order while select to merge mesh to make them center.
def sortmesh(selectmesh):
print("MESH SORTING...")
@@ -980,7 +979,7 @@ def sortmesh(selectmesh):
else:#if not add here for not center
notcentermesh.append(selectmesh[countm])
selectmesh = []
- #add mesh object in order for merge object
+ #add mesh object in order for merge object
for countm in range(len(centermesh)):
selectmesh.append(centermesh[countm])
for countm in range(len(notcentermesh)):
@@ -1007,10 +1006,10 @@ def parse_mesh( mesh, psk ):
bpy.context.scene.objects.unlink(setmesh)
#print("FACES----:",len(mesh.data.tessfaces))
verbose("Working mesh object: {}".format(mesh.name))
-
+
#collect a list of the material names
print("Materials...")
-
+
mat_slot_index = 0
for slot in mesh.material_slots:
@@ -1019,7 +1018,7 @@ def parse_mesh( mesh, psk ):
MaterialName.append(slot.name)
#if slot.material.texture_slots[0] != None:
#if slot.material.texture_slots[0].texture.image.filepath != None:
- #print(" Texture path {}".format(slot.material.texture_slots[0].texture.image.filepath))
+ #print(" Texture path {}".format(slot.material.texture_slots[0].texture.image.filepath))
#create the current material
v_material = psk.GetMatByIndex(mat_slot_index)
v_material.MaterialName = slot.name
@@ -1029,7 +1028,7 @@ def parse_mesh( mesh, psk ):
verbose(" PSK index {}".format(v_material.TextureIndex))
#END slot in mesh.material_slots
-
+
# object_mat = mesh.materials[0]
#object_material_index = mesh.active_material_index
#FIXME ^ this is redundant due to "= face.material_index" in face loop
@@ -1037,19 +1036,19 @@ def parse_mesh( mesh, psk ):
wedges = ObjMap()
points = ObjMap() #vertex
points_linked = {}
-
+
discarded_face_count = 0
sys.setrecursionlimit(1000000)
smoothgroup_list = parse_smooth_groups(mesh.data)
-
+
print("{} faces".format(len(mesh.data.tessfaces)))
-
+
print("Smooth groups active:", bpy.context.scene.udk_option_smoothing_groups)
-
+
for face in mesh.data.tessfaces:
-
+
smoothgroup_id = 0x80000000
-
+
for smooth_group in smoothgroup_list:
if smooth_group.contains_face(face):
smoothgroup_id = smooth_group.id
@@ -1059,38 +1058,38 @@ def parse_mesh( mesh, psk ):
#print current_face.uv_textures
# modified by VendorX
object_material_index = face.material_index
-
+
if len(face.vertices) != 3:
raise Error("Non-triangular face (%i)" % len(face.vertices))
-
- #RG - apparently blender sometimes has problems when you do quad to triangle
+
+ #RG - apparently blender sometimes has problems when you do quad to triangle
# conversion, and ends up creating faces that have only TWO points -
- # one of the points is simply in the vertex list for the face twice.
- # This is bad, since we can't get a real face normal for a LINE, we need
- # a plane for this. So, before we add the face to the list of real faces,
- # ensure that the face is actually a plane, and not a line. If it is not
+ # one of the points is simply in the vertex list for the face twice.
+ # This is bad, since we can't get a real face normal for a LINE, we need
+ # a plane for this. So, before we add the face to the list of real faces,
+ # ensure that the face is actually a plane, and not a line. If it is not
# planar, just discard it and notify the user in the console after we're
# done dumping the rest of the faces
-
+
if not is_1d_face(face, mesh.data):
-
+
wedge_list = []
vect_list = []
-
+
#get or create the current material
psk.GetMatByIndex(object_material_index)
face_index = face.index
has_uv = False
face_uv = None
-
+
if len(mesh.data.uv_textures) > 0:
- has_uv = True
+ has_uv = True
uv_layer = mesh.data.tessface_uv_textures.active
face_uv = uv_layer.data[face_index]
#size(data) is number of texture faces. Each face has UVs
#print("DATA face uv: ",len(faceUV.uv), " >> ",(faceUV.uv[0][0]))
-
+
for i in range(3):
vert_index = face.vertices[i]
vert = mesh.data.vertices[vert_index]
@@ -1105,11 +1104,11 @@ def parse_mesh( mesh, psk ):
else:
#print ("No UVs?")
uv = [0.0, 0.0]
-
+
#flip V coordinate because UEd requires it and DOESN'T flip it on its own like it
#does with the mesh Y coordinates. this is otherwise known as MAGIC-2
uv[1] = 1.0 - uv[1]
-
+
# clamp UV coords if udk_option_clamp_uv is True
if bpy.context.scene.udk_option_clamp_uv:
if (uv[0] > 1):
@@ -1120,11 +1119,11 @@ def parse_mesh( mesh, psk ):
uv[1] = 1
if (uv[1] < 0):
uv[1] = 0
-
+
# RE - Append untransformed vector (for normal calc below)
# TODO: convert to Blender.Mathutils
vect_list.append( FVector(vert.co.x, vert.co.y, vert.co.z) )
-
+
# Transform position for export
#vpos = vert.co * object_material_index
@@ -1148,13 +1147,13 @@ def parse_mesh( mesh, psk ):
lPoint.Point.X = vpos.x
lPoint.Point.Y = vpos.y
lPoint.Point.Z = vpos.z
-
+
if lPoint in points_linked:
if not(p in points_linked[lPoint]):
points_linked[lPoint].append(p)
else:
points_linked[lPoint] = [p]
-
+
# Create the wedge
w = VVertex()
w.MatIndex = object_material_index
@@ -1165,18 +1164,18 @@ def parse_mesh( mesh, psk ):
w.SmoothGroup = smoothgroup_id
index_wedge = wedges.get(w)
wedge_list.append(index_wedge)
-
+
#print results
#print("result PointIndex={}, U={:.6f}, V={:.6f}, wedge_index={}".format(
# w.PointIndex,
# w.U,
# w.V,
# index_wedge))
-
+
#END for i in range(3)
# Determine face vertex order
-
+
# TODO: convert to Blender.Mathutils
# get normal from blender
no = face.normal
@@ -1202,13 +1201,13 @@ def parse_mesh( mesh, psk ):
dindex0 = face.vertices[0];
dindex1 = face.vertices[1];
dindex2 = face.vertices[2];
-
+
mesh.data.vertices[dindex0].select = True
mesh.data.vertices[dindex1].select = True
mesh.data.vertices[dindex2].select = True
-
+
raise Error("Normal coplanar with face! points:", mesh.data.vertices[dindex0].co, mesh.data.vertices[dindex1].co, mesh.data.vertices[dindex2].co)
-
+
face.select = True
if face.use_smooth == True:
tri.SmoothingGroups = 1
@@ -1219,53 +1218,53 @@ def parse_mesh( mesh, psk ):
if bpy.context.scene.udk_option_smoothing_groups:
tri.SmoothingGroups = smoothgroup_id
print("Bool Smooth")
-
+
psk.AddFace(tri)
- #END if not is_1d_face(current_face, mesh.data)
+ #END if not is_1d_face(current_face, mesh.data)
else:
discarded_face_count += 1
-
+
#END face in mesh.data.faces
-
+
print("{} points".format(len(points.dict)))
-
+
for point in points.items():
psk.AddPoint(point)
-
+
if len(points.dict) > 32767:
raise Error("Mesh vertex limit exceeded! {} > 32767".format(len(points.dict)))
-
+
print("{} wedges".format(len(wedges.dict)))
-
+
for wedge in wedges.items():
psk.AddWedge(wedge)
-
+
# alert the user to degenerate face issues
if discarded_face_count > 0:
print("WARNING: Mesh contained degenerate faces (non-planar)")
print(" Discarded {} faces".format(discarded_face_count))
-
- #RG - walk through the vertex groups and find the indexes into the PSK points array
- #for them, then store that index and the weight as a tuple in a new list of
+
+ #RG - walk through the vertex groups and find the indexes into the PSK points array
+ #for them, then store that index and the weight as a tuple in a new list of
#verts for the group that we can look up later by bone name, since Blender matches
#verts to bones for influences by having the VertexGroup named the same thing as
#the bone
-
- #[print(x, len(points_linked[x])) for x in points_linked]
+
+ #[print(x, len(points_linked[x])) for x in points_linked]
#print("pointsindex length ",len(points_linked))
#vertex group
-
+
# all vertex groups of the mesh (obj)...
for obj_vertex_group in mesh.vertex_groups:
-
+
#print(" bone group build:",obj_vertex_group.name)#print bone name
#print(dir(obj_vertex_group))
verbose("obj_vertex_group.name={}".format(obj_vertex_group.name))
-
+
vertex_list = []
-
+
# all vertices in the mesh...
for vertex in mesh.data.vertices:
#print(dir(vertex))
@@ -1280,7 +1279,7 @@ def parse_mesh( mesh, psk ):
vpos.y = vpos.y * bpy.context.scene.udk_option_scale
vpos.z = vpos.z * bpy.context.scene.udk_option_scale
p.Point.X = vpos.x
- p.Point.Y = vpos.y
+ p.Point.Y = vpos.y
p.Point.Z = vpos.z
#print(p)
#print(len(points_linked[p]))
@@ -1292,11 +1291,11 @@ def parse_mesh( mesh, psk ):
except Exception:#if get error ignore them #not safe I think
print("Error link points!")
pass
-
+
#bone name, [point id and wieght]
#print("Add Vertex Group:",obj_vertex_group.name, " No. Points:",len(vertex_list))
psk.VertexGroups[obj_vertex_group.name] = vertex_list
-
+
# remove the temporary triangulated mesh
if bpy.context.scene.udk_option_triangulate == True:
verbose("Removing temporary triangle mesh: {}".format(mesh.name))
@@ -1308,34 +1307,34 @@ def parse_mesh( mesh, psk ):
# Collate bones that belong to the UDK skeletal mesh
#===========================================================================
def parse_armature( armature, psk, psa ):
-
+
print(header("ARMATURE", 'RIGHT'))
verbose("Armature object: {} Armature data: {}".format(armature.name, armature.data.name))
-
+
# generate a list of root bone candidates
root_candidates = [b for b in armature.data.bones if b.parent == None and b.use_deform == True]
-
+
# should be a single, unambiguous result
if len(root_candidates) == 0:
raise Error("Cannot find root for UDK bones. The root bone must use deform.")
-
+
if len(root_candidates) > 1:
raise Error("Ambiguous root for UDK. More than one root bone is using deform.")
-
+
# prep for bone collection
udk_root_bone = root_candidates[0]
udk_bones = []
BoneUtil.static_bone_id = 0 # replaces global
-
+
# traverse bone chain
print("{: <3} {: <48} {: <20}".format("ID", "Bone", "Status"))
print()
recurse_bone(udk_root_bone, udk_bones, psk, psa, 0, armature.matrix_local)
-
+
# final validation
if len(udk_bones) < 3:
raise Error("Less than three bones may crash UDK (legacy issue?)")
-
+
# return a list of bones making up the entire udk skel
# this is passed to parse_animation instead of working from keyed bones in the action
return udk_bones
@@ -1350,14 +1349,14 @@ def parse_armature( armature, psk, psa ):
# indent text indent for recursive log
#===========================================================================
def recurse_bone( bone, bones, psk, psa, parent_id, parent_matrix, indent="" ):
-
+
status = "Ok"
-
+
bones.append(bone);
if not bone.use_deform:
status = "No effect"
-
+
# calc parented bone transform
if bone.parent != None:
quat = make_fquat(bone.matrix.to_quaternion())
@@ -1378,12 +1377,12 @@ def recurse_bone( bone, bones, psk, psa, parent_id, parent_matrix, indent="" ):
translation.z = translation.z * bpy.context.scene.udk_option_scale
bone_id = BoneUtil.static_bone_id # ALT VERS
BoneUtil.static_bone_id += 1 # ALT VERS
-
+
child_count = len(bone.children)
-
+
psk.AddBone( make_vbone(bone.name, parent_id, child_count, quat, translation) )
psa.StoreBone( make_namedbonebinary(bone.name, parent_id, child_count, quat, translation, 1) )
-
+
#RG - dump influences for this bone - use the data we collected in the mesh dump phase to map our bones to vertex groups
if bone.name in psk.VertexGroups:
vertex_list = psk.VertexGroups[bone.name]
@@ -1400,12 +1399,12 @@ def recurse_bone( bone, bones, psk, psa, parent_id, parent_matrix, indent="" ):
else:
status = "No vertex group"
#FIXME overwriting previous status error?
-
+
print("{:<3} {:<48} {:<20}".format(bone_id, indent+bone.name, status))
-
+
#bone.matrix_local
#recursively dump child bones
-
+
for child_bone in bone.children:
recurse_bone(child_bone, bones, psk, psa, bone_id, parent_matrix, " "+indent)
@@ -1420,12 +1419,12 @@ class BoneUtil:
# psa the PSA file object
#===========================================================================
def parse_animation( armature, udk_bones, actions_to_export, psa ):
-
+
print(header("ANIMATION", 'RIGHT'))
-
+
context = bpy.context
anim_rate = context.scene.render.fps
-
+
verbose("Armature object: {}".format(armature.name))
print("Scene: {} FPS: {} Frames: {} to {}".format(context.scene.name, anim_rate, context.scene.frame_start, context.scene.frame_end))
print("Processing {} action(s)".format(len(actions_to_export)))
@@ -1434,13 +1433,13 @@ def parse_animation( armature, udk_bones, actions_to_export, psa ):
print("None Actions Set! skipping...")
return
restoreAction = armature.animation_data.action # Q: is animation_data always valid?
-
+
restoreFrame = context.scene.frame_current # we already do this in export_proxy, but we'll do it here too for now
raw_frame_index = 0 # used to set FirstRawFrame, seperating actions in the raw keyframe array
-
+
# action loop...
for action in actions_to_export:
-
+
# removed: check for armature with no animation; all it did was force you to add one
if not len(action.fcurves):
@@ -1451,14 +1450,14 @@ def parse_animation( armature, udk_bones, actions_to_export, psa ):
# note if loop all actions that is not armature it will override and will break armature animation.
armature.animation_data.action = action
context.scene.update()
-
+
# min/max frames define range
framemin, framemax = action.frame_range
start_frame = int(framemin)
end_frame = int(framemax)
scene_range = range(start_frame, end_frame + 1)
frame_count = len(scene_range)
-
+
# create the AnimInfoBinary
anim = AnimInfoBinary()
anim.Name = action.name
@@ -1466,11 +1465,11 @@ def parse_animation( armature, udk_bones, actions_to_export, psa ):
anim.NumRawFrames = frame_count
anim.AnimRate = anim_rate
anim.FirstRawFrame = raw_frame_index
-
+
print("{}, frames {} to {} ({} frames)".format(action.name, start_frame, end_frame, frame_count))
-
+
# removed: bone lookup table
-
+
# build a list of pose bones relevant to the collated udk_bones
# fixme: could be done once, prior to loop?
udk_pose_bones = []
@@ -1483,39 +1482,39 @@ def parse_animation( armature, udk_bones, actions_to_export, psa ):
# sort in the order the bones appear in the PSA file
ordered_bones = {}
ordered_bones = sorted([(psa.UseBone(b.name), b) for b in udk_pose_bones], key=operator.itemgetter(0))
-
+
# NOTE: posebone.bone references the obj/edit bone
# REMOVED: unique_bone_indexes is redundant?
-
+
# frame loop...
for i in range(frame_count):
-
+
frame = scene_range[i]
-
+
#verbose("FRAME {}".format(i), i) # test loop sampling
-
+
# advance to frame (automatically updates the pose)
context.scene.frame_set(frame)
-
+
# compute the key for each bone
for bone_data in ordered_bones:
-
+
bone_index = bone_data[0]
pose_bone = bone_data[1]
pose_bone_matrix = mathutils.Matrix(pose_bone.matrix)
-
+
if pose_bone.parent != None:
pose_bone_parent_matrix = mathutils.Matrix(pose_bone.parent.matrix)
pose_bone_matrix = pose_bone_parent_matrix.inverted() * pose_bone_matrix
-
+
head = pose_bone_matrix.to_translation()
quat = pose_bone_matrix.to_quaternion().normalized()
-
+
if pose_bone.parent != None:
quat = make_fquat(quat)
else:
quat = make_fquat_default(quat)
-
+
#scale animation position here?
if bpy.context.scene.udk_option_scale < 0 or bpy.context.scene.udk_option_scale > 1:
head.x = head.x * bpy.context.scene.udk_option_scale
@@ -1527,25 +1526,25 @@ def parse_animation( armature, udk_bones, actions_to_export, psa ):
vkey.Position.Y = head.y
vkey.Position.Z = head.z
vkey.Orientation = quat
-
+
# frame delta = 1.0 / fps
vkey.Time = 1.0 / float(anim_rate) # according to C++ header this is "disregarded"
-
+
psa.AddRawKey(vkey)
-
+
# END for bone_data in ordered_bones
raw_frame_index += 1
-
+
# END for i in range(frame_count)
-
+
anim.TotalBones = len(ordered_bones) # REMOVED len(unique_bone_indexes)
anim.TrackTime = float(frame_count) # frame_count/anim.AnimRate makes more sense, but this is what actually works in UDK
verbose("anim.TotalBones={}, anim.TrackTime={}".format(anim.TotalBones, anim.TrackTime))
-
+
psa.AddAnimation(anim)
-
+
# END for action in actions
# restore
@@ -1560,7 +1559,7 @@ def parse_animation( armature, udk_bones, actions_to_export, psa ):
def collate_actions():
verbose(header("collate_actions"))
actions_to_export = []
-
+
for action in bpy.data.actions:
if bpy.context.scene.udk_option_selectanimations: # check if needed to select actions set for exporting it
print("Action Set is selected!")
@@ -1575,7 +1574,7 @@ def collate_actions():
continue
verbose(" + {}".format(action.name)) #action set name
actions_to_export.append(action) #add to the action array
-
+
return actions_to_export
#===========================================================================
@@ -1584,16 +1583,16 @@ def collate_actions():
#===========================================================================
def find_armature_and_mesh():
verbose(header("find_armature_and_mesh", 'LEFT', '<', 60))
-
+
context = bpy.context
active_object = context.active_object
armature = None
mesh = None
-
+
# TODO:
# this could be more intuitive
#bpy.ops.object.mode_set(mode='OBJECT')
-
+
if bpy.context.scene.udk_option_selectobjects: #if checked select object true do list object on export
print("select mode:")
if len(bpy.context.scene.udkArm_list) > 0:
@@ -1626,14 +1625,14 @@ def find_armature_and_mesh():
mesh = active_object
else:
raise Error("The selected mesh is not parented to the armature")
-
+
# otherwise, expect a single mesh parented to the armature (other object types are ignored)
else:
print("Number of meshes:",len(meshes))
print("Number of meshes (selected):",len(meshes))
if len(meshes) == 1:
mesh = meshes[0]
-
+
elif len(meshes) > 1:
if len(meshselected) >= 1:
mesh = sortmesh(meshselected)
@@ -1651,7 +1650,7 @@ def find_armature_and_mesh():
else:
#bpy.ops.object.mode_set(mode='OBJECT')
all_armatures = [obj for obj in bpy.context.scene.objects if obj.type == 'ARMATURE']
-
+
if len(all_armatures) == 1:#if armature has one scene just assign it
armature = all_armatures[0]
elif len(all_armatures) > 1:#if there more armature then find the select armature
@@ -1665,15 +1664,15 @@ def find_armature_and_mesh():
raise Error("Please select an armatures in the scene")
else:
raise Error("No armatures in scene")
-
+
verbose("Found armature: {}".format(armature.name))
-
+
meshselected = []
parented_meshes = [obj for obj in armature.children if obj.type == 'MESH']
-
+
if len(armature.children) == 0:
raise Error("The selected Armature has no mesh parented to the Armature Object!")
-
+
for obj in armature.children:
#print(dir(obj))
if obj.type == 'MESH' and obj.select == True:
@@ -1684,14 +1683,14 @@ def find_armature_and_mesh():
mesh = active_object
else:
raise Error("The selected mesh is not parented to the armature")
-
+
# otherwise, expect a single mesh parented to the armature (other object types are ignored)
else:
print("Number of meshes:",len(parented_meshes))
print("Number of meshes (selected):",len(meshselected))
if len(parented_meshes) == 1:
mesh = parented_meshes[0]
-
+
elif len(parented_meshes) > 1:
if len(meshselected) >= 1:
mesh = sortmesh(meshselected)
@@ -1699,7 +1698,7 @@ def find_armature_and_mesh():
raise Error("More than one mesh(s) parented to armature. Select object(s)!")
else:
raise Error("No mesh parented to armature")
-
+
verbose("Found mesh: {}".format(mesh.name))
if mesh == None or armature == None:
raise Error("Check Mesh and Armature are list!")
@@ -1707,7 +1706,7 @@ def find_armature_and_mesh():
#print("Armature and Mesh Vertex Groups matches Ok!")
#else:
#raise Error("Armature bones:" + str(len(armature.pose.bones)) + " Mesh Vertex Groups:" + str(len(mesh.vertex_groups)) +" doesn't match!")
-
+
#this will check if object need to be rebuild.
if bpy.context.scene.udk_option_rebuildobjects:
#print("INIT... REBUILDING...")
@@ -1727,14 +1726,14 @@ def find_armature_and_mesh():
def collate_vertex_groups( mesh ):
verbose("collate_vertex_groups")
groups = []
-
+
for group in mesh.vertex_groups:
-
+
groups.append(group)
verbose(" " + group.name)
-
+
return groups
-
+
#===========================================================================
# Main
#===========================================================================
@@ -1743,72 +1742,72 @@ def export(filepath):
bpy.types.Scene.udk_copy_merge = False #in case fail to export set this to default
t = time.clock()
context = bpy.context
-
+
print("Blender Version {}.{}.{}".format(bpy.app.version[0], bpy.app.version[1], bpy.app.version[2]))
print("Filepath: {}".format(filepath))
-
+
verbose("PSK={}, PSA={}".format(context.scene.udk_option_export_psk, context.scene.udk_option_export_psa))
-
+
# find armature and mesh
# [change this to implement alternative methods; raise Error() if not found]
udk_armature, udk_mesh = find_armature_and_mesh()
-
+
# check misc conditions
if not (udk_armature.scale.x == udk_armature.scale.y == udk_armature.scale.z == 1):
raise Error("bad armature scale: armature object should have uniform scale of 1 (ALT-S)")
-
+
if not (udk_mesh.scale.x == udk_mesh.scale.y == udk_mesh.scale.z == 1):
raise Error("bad mesh scale: mesh object should have uniform scale of 1 (ALT-S)")
-
+
if not (udk_armature.location.x == udk_armature.location.y == udk_armature.location.z == 0):
raise Error("bad armature location: armature should be located at origin (ALT-G)")
-
+
if not (udk_mesh.location.x == udk_mesh.location.y == udk_mesh.location.z == 0):
raise Error("bad mesh location: mesh should be located at origin (ALT-G)")
-
+
# prep
psk = PSKFile()
psa = PSAFile()
-
+
# step 1
parse_mesh(udk_mesh, psk)
-
+
# step 2
udk_bones = parse_armature(udk_armature, psk, psa)
-
+
# step 3
if context.scene.udk_option_export_psa == True:
actions = collate_actions()
parse_animation(udk_armature, udk_bones, actions, psa)
-
+
# write files
print(header("Exporting", 'CENTER'))
-
+
psk_filename = filepath + '.psk'
psa_filename = filepath + '.psa'
-
+
if context.scene.udk_option_export_psk == True:
print("Skeletal mesh data...")
psk.PrintOut()
- file = open(psk_filename, "wb")
+ file = open(psk_filename, "wb")
file.write(psk.dump())
- file.close()
+ file.close()
print("Exported: " + psk_filename)
print()
-
+
if context.scene.udk_option_export_psa == True:
print("Animation data...")
if not psa.IsEmpty():
psa.PrintOut()
- file = open(psa_filename, "wb")
+ file = open(psa_filename, "wb")
file.write(psa.dump())
- file.close()
+ file.close()
print("Exported: " + psa_filename)
else:
print("No Animation (.psa file) to export")
print()
-
+
#if objects are rebuild do the unlink
if bpy.context.scene.udk_option_rebuildobjects:
print("Unlinking Objects")
@@ -1826,20 +1825,20 @@ class Operator_UDKExport( bpy.types.Operator ):
"""Export to UDK"""
bl_idname = "object.udk_export"
bl_label = "Export now"
-
+
def execute(self, context):
print( "\n"*8 )
-
+
scene = bpy.context.scene
-
+
scene.udk_option_export_psk = (scene.udk_option_export == '0' or scene.udk_option_export == '2')
scene.udk_option_export_psa = (scene.udk_option_export == '1' or scene.udk_option_export == '2')
-
+
filepath = get_dst_path()
-
+
# cache settings
restore_frame = scene.frame_current
-
+
message = "Finish Export!"
try:
export(filepath)
@@ -1847,15 +1846,15 @@ class Operator_UDKExport( bpy.types.Operator ):
except Error as err:
print(err.message)
message = err.message
-
+
# restore settings
scene.frame_set(restore_frame)
-
+
self.report({'ERROR'}, message)
-
+
# restore settings
scene.frame_set(restore_frame)
-
+
return {'FINISHED'}
#===========================================================================
@@ -1865,7 +1864,7 @@ class Operator_ToggleConsole( bpy.types.Operator ):
"""Show or hide the console"""
bl_idname = "object.toggle_console"
bl_label = "Toggle console"
-
+
#def invoke(self, context, event):
# bpy.ops.wm.console_toggle()
# return{'FINISHED'}
@@ -1894,7 +1893,7 @@ bpy.types.Scene.udk_option_filename_src = EnumProperty(
items = [ ('0', "From object", "Name will be taken from object name"),
('1', "From Blend", "Name will be taken from .blend file name") ],
default = '0')
-
+
bpy.types.Scene.udk_option_export_psk = BoolProperty(
name = "bool export psa",
description = "Boolean for exporting psk format (Skeleton Mesh)",
@@ -1909,7 +1908,7 @@ bpy.types.Scene.udk_option_clamp_uv = BoolProperty(
name = "Clamp UV",
description = "True is to limit Clamp UV co-ordinates to [0-1]. False is unrestricted (x,y). ",
default = False)
-
+
bpy.types.Scene.udk_copy_merge = BoolProperty(
name = "Merge Mesh",
description = "This will copy the mesh(s) and merge the object together and unlink the mesh to be remove while exporting the object.",
@@ -1937,22 +1936,22 @@ bpy.types.Scene.udk_option_triangulate = BoolProperty(
name = "Triangulate Mesh",
description = "Convert Quads to Triangles",
default = False)
-
+
bpy.types.Scene.udk_option_selectanimations = BoolProperty(
name = "Select Animation(s)",
description = "Select animation(s) for export to psa file.",
default = False)
-
+
bpy.types.Scene.udk_option_selectobjects = BoolProperty(
name = "Select Object(s)",
description = "Select Armature and Mesh(s). Just make sure mesh(s) is parent to armature.",
default = False)
-
+
bpy.types.Scene.udk_option_rebuildobjects = BoolProperty(
name = "Rebuild Objects",
description = "In case of deform skeleton mesh and animations data. This will rebuild objects from raw format on export when checked.",
default = False)
-
+
bpy.types.Scene.udk_option_ignoreactiongroupnames = BoolProperty(
name = "Ignore Action Group Names",
description = "This will Ignore Action Set Group Names Check With Armature Bones. It will override armature to set action set.",
@@ -1970,7 +1969,7 @@ class OBJECT_OT_UTSelectedFaceSmooth(bpy.types.Operator):
"""It will only select smooth faces that is select mesh"""
bl_idname = "object.utselectfacesmooth" # XXX, name???
bl_label = "Select Smooth Faces"#"Select Smooth faces"
-
+
def invoke(self, context, event):
print("----------------------------------------")
print("Init Select Face(s):")
@@ -2006,21 +2005,21 @@ class OBJECT_OT_UTSelectedFaceSmooth(bpy.types.Operator):
else:
print("Didn't select Mesh Object!")
self.report({'INFO'}, "Didn't Select Mesh Object!")
- print("----------------------------------------")
+ print("----------------------------------------")
return{'FINISHED'}
-
+
class OBJECT_OT_MeshClearWeights(bpy.types.Operator):
"""Remove all mesh vertex groups weights for the bones."""
bl_idname = "object.meshclearweights" # XXX, name???
bl_label = "Remove Vertex Weights"#"Remove Mesh vertex weights"
-
+
def invoke(self, context, event):
for obj in bpy.data.objects:
if obj.type == 'MESH' and obj.select == True:
for vg in obj.vertex_groups:
obj.vertex_groups.remove(vg)
self.report({'INFO'}, "Mesh Vertex Groups Remove!")
- break
+ break
return{'FINISHED'}
def unpack_list(list_of_tuples):
@@ -2036,7 +2035,7 @@ def rebuildmesh(obj):
for i in bpy.context.scene.objects: i.select = False #deselect all objects
obj.select = True
bpy.context.scene.objects.active = obj
-
+
me_ob = bpy.data.meshes.new(("Re_"+obj.name))
mesh = obj.data
faces = []
@@ -2061,7 +2060,7 @@ def rebuildmesh(obj):
faces.extend([(face.vertices[0],face.vertices[1],face.vertices[2],face.vertices[3])])
#vertex positions
for vertex in mesh.vertices:
- verts.append(vertex.co.to_tuple())
+ verts.append(vertex.co.to_tuple())
#vertices weight groups into array
vertGroups = {} #array in strings
for vgroup in obj.vertex_groups:
@@ -2072,15 +2071,15 @@ def rebuildmesh(obj):
vlist.append((v.index,vg.weight))
#print((v.index,vg.weight))
vertGroups[vgroup.name] = vlist
-
+
#print("creating mesh object...")
#me_ob.from_pydata(verts, [], faces)
me_ob.vertices.add(len(verts))
me_ob.tessfaces.add(len(faces))
- me_ob.vertices.foreach_set("co", unpack_list(verts))
+ me_ob.vertices.foreach_set("co", unpack_list(verts))
me_ob.tessfaces.foreach_set("vertices_raw",unpack_list( faces))
me_ob.tessfaces.foreach_set("use_smooth", smoothings)#smooth array from face
-
+
#check if there is uv faces
if len(uvfaces) > 0:
uvtex = me_ob.tessface_uv_textures.new(name="retex")
@@ -2096,7 +2095,7 @@ def rebuildmesh(obj):
blender_tface.uv2 = mfaceuv[1];
blender_tface.uv3 = mfaceuv[2];
blender_tface.uv4 = mfaceuv[3];
-
+
me_ob.update()#need to update the information to able to see into the secne
obmesh = bpy.data.objects.new(("Re_"+obj.name),me_ob)
bpy.context.scene.update()
@@ -2129,7 +2128,7 @@ class OBJECT_OT_UTRebuildMesh(bpy.types.Operator):
"""Note the scale will be 1:1 for object mode. To keep from deforming"""
bl_idname = "object.utrebuildmesh" # XXX, name???
bl_label = "Rebuild Mesh"#"Rebuild Mesh"
-
+
def invoke(self, context, event):
print("----------------------------------------")
print("Init Mesh Bebuild...")
@@ -2155,7 +2154,7 @@ def rebuildarmature(obj):
for i in bpy.context.scene.objects: i.select = False #deselect all objects
ob_new.select = True
bpy.context.scene.objects.active = obj
-
+
bpy.ops.object.mode_set(mode='EDIT')
for bone in obj.data.edit_bones:
if bone.parent != None:
@@ -2166,7 +2165,7 @@ def rebuildarmature(obj):
for i in bpy.context.scene.objects: i.select = False #deselect all objects
bpy.context.scene.objects.active = ob_new
bpy.ops.object.mode_set(mode='EDIT')
-
+
for bone in obj.data.bones:
bpy.ops.object.mode_set(mode='EDIT')
newbone = ob_new.data.edit_bones.new(bone.name)
@@ -2185,14 +2184,14 @@ def rebuildarmature(obj):
ob_new.animation_data.action = obj.animation_data.action #just make sure it here to do the animations if exist
print("Armature Object Name:",ob_new.name)
return ob_new
-
+
class OBJECT_OT_UTRebuildArmature(bpy.types.Operator):
"""If mesh is deform when importing to unreal engine try this. """ \
"""It rebuild the bones one at the time by select one armature object scrape to raw setup build. """ \
"""Note the scale will be 1:1 for object mode. To keep from deforming"""
bl_idname = "object.utrebuildarmature" # XXX, name???
bl_label = "Rebuild Armature" #Rebuild Armature
-
+
def invoke(self, context, event):
print("----------------------------------------")
print("Init Rebuild Armature...")
@@ -2280,12 +2279,12 @@ class Panel_UDKExport( bpy.types.Panel ):
#bl_context = "object"
bl_space_type = "VIEW_3D"
bl_region_type = "TOOLS"
-
+
#def draw_header(self, context):
# layout = self.layout
#obj = context.object
#layout.prop(obj, "select", text="")
-
+
#@classmethod
#def poll(cls, context):
# return context.active_object
@@ -2312,7 +2311,7 @@ class Panel_UDKExport( bpy.types.Panel ):
#layout.separator()
layout.prop(context.scene, "udk_option_export")
layout.prop(context.scene, "udk_option_selectobjects")
-
+
if context.scene.udk_option_selectobjects:
layout.operator("object.selobjectpdate")
layout.label(text="ARMATURE - Index")
@@ -2335,7 +2334,7 @@ class Panel_UDKExport( bpy.types.Panel ):
row11.operator("object.udk_export")
row11.operator("object.toggle_console")
layout.operator(OBJECT_OT_UTRebuildArmature.bl_idname)
- layout.label(text="Mesh")
+ layout.label(text="Mesh")
layout.operator(OBJECT_OT_MeshClearWeights.bl_idname)
layout.operator(OBJECT_OT_UTSelectedFaceSmooth.bl_idname)
layout.operator(OBJECT_OT_UTRebuildMesh.bl_idname)
@@ -2377,7 +2376,7 @@ def udkupdateobjects():
my_objlist.remove(count)
break
count += 1
-
+
my_objlist = bpy.context.scene.udkmesh_list
objectl = []
for objarm in bpy.context.scene.objects:#list and filter only mesh and armature
@@ -2412,14 +2411,14 @@ def udkupdateobjects():
my_objlist.remove(count)
break
count += 1
-
+
class OBJECT_OT_UDKObjUpdate(bpy.types.Operator):
"""This will update the filter of the mesh and armature."""
bl_idname = "object.selobjectpdate"
bl_label = "Update Object(s)"
-
+
actionname = bpy.props.StringProperty()
-
+
def execute(self, context):
udkupdateobjects()
return{'FINISHED'}
@@ -2439,7 +2438,7 @@ def udkcheckmeshline():
bpy.ops.object.mode_set(mode='EDIT') #set in edit mode
bpy.ops.mesh.select_all(action='DESELECT')
bpy.context.tool_settings.mesh_select_mode = (True, False, False) #select vertices
-
+
if objmesh != None:
print("found mesh")
print(objmesh)
@@ -2465,7 +2464,7 @@ def udkcheckmeshline():
norm = FVector(no[0], no[1], no[2])
tnorm = vect_list[1].sub(vect_list[0]).cross(vect_list[2].sub(vect_list[1]))
dot = norm.dot(tnorm)
-
+
tri = VTriangle()
if dot > 0:
(tri.WedgeIndex2, tri.WedgeIndex1, tri.WedgeIndex0) = wedge_list
@@ -2478,7 +2477,7 @@ def udkcheckmeshline():
vertex_list.append(dindex0)
vertex_list.append(dindex1)
vertex_list.append(dindex2)
-
+
bpy.ops.object.mode_set(mode='OBJECT')
for vertex in objmesh.data.vertices: #loop all vertex in the mesh list
for vl in vertex_list: #loop for error vertex
@@ -2498,7 +2497,7 @@ class OBJECT_OT_UDKCheckMeshLines(bpy.types.Operator):
"""If the vertices share the same position it will causes an bug."""
bl_idname = "object.udkcheckmeshline"
bl_label = "Check Mesh Vertices"
-
+
def execute(self, context):
message = udkcheckmeshline()
self.report({'ERROR'}, message)
@@ -2511,10 +2510,10 @@ class OBJECT_OT_ActionSetAnimUpdate(bpy.types.Operator):
bl_label = "Update Action Set(s)"
actionname = bpy.props.StringProperty()
-
+
def execute(self, context):
my_sett = bpy.context.scene.udkas_list
-
+
bones = []
armature = None
armatures = []
@@ -2525,14 +2524,14 @@ class OBJECT_OT_ActionSetAnimUpdate(bpy.types.Operator):
armatures.append(objarm)
if objarm.select == True:
armatureselected.append(objarm)
-
+
if len(armatureselected) == len(armatures) == 1:
armature = armatures[0]
if len(armatures) == 1:
armature = armatures[0]
- if len(armatureselected) == 1:
+ if len(armatureselected) == 1:
armature = armatureselected[0]
-
+
if armature != None:
for bone in armature.pose.bones:
bones.append(bone.name)
@@ -2565,7 +2564,7 @@ class OBJECT_OT_ActionSetAnimUpdate(bpy.types.Operator):
my_item.bmatch = True
else:
my_item.bmatch = False
- removeactions = []
+ removeactions = []
#check action list and data actions
for actionlist in bpy.context.scene.udkas_list:
bfind = False
@@ -2578,7 +2577,7 @@ class OBJECT_OT_ActionSetAnimUpdate(bpy.types.Operator):
#print("ACT NAME:",actionlist.name," COUNT",notfound)
if notfound == len(bpy.data.actions):
#print("remove :",actionlist.name)
- removeactions.append(actionlist.name)
+ removeactions.append(actionlist.name)
#print("Not in the action data list:",len(removeactions))
#remove list or chnages in the name the template list
for actname in removeactions:
@@ -2589,8 +2588,8 @@ class OBJECT_OT_ActionSetAnimUpdate(bpy.types.Operator):
my_sett.remove(actioncount);
break
actioncount += 1
- return{'FINISHED'}
-
+ return{'FINISHED'}
+
class ExportUDKAnimData(bpy.types.Operator):
"""Export Skeleton Mesh / Animation Data file(s). """ \
"""One mesh and one armature else select one mesh or armature to be exported"""
@@ -2614,7 +2613,7 @@ class ExportUDKAnimData(bpy.types.Operator):
udk_option_export = bpy.types.Scene.udk_option_export
udk_option_scale = bpy.types.Scene.udk_option_scale
udk_option_rebuildobjects = bpy.types.Scene.udk_option_rebuildobjects
-
+
@classmethod
def poll(cls, context):
return context.active_object != None
@@ -2625,12 +2624,12 @@ class ExportUDKAnimData(bpy.types.Operator):
scene.udk_option_export_psa = (scene.udk_option_export == '1' or scene.udk_option_export == '2')
bpy.context.scene.udk_option_scale = self.udk_option_scale
bpy.context.scene.udk_option_rebuildobjects = self.udk_option_rebuildobjects
-
+
filepath = get_dst_path()
-
+
# cache settings
restore_frame = scene.frame_current
-
+
message = "Finish Export!"
try:
export(filepath)
@@ -2638,18 +2637,18 @@ class ExportUDKAnimData(bpy.types.Operator):
except Error as err:
print(err.message)
message = err.message
-
+
# restore settings
scene.frame_set(restore_frame)
-
+
self.report({'WARNING', 'INFO'}, message)
return {'FINISHED'}
-
+
def invoke(self, context, event):
wm = context.window_manager
wm.fileselect_add(self)
return {'RUNNING_MODAL'}
-
+
def menu_func(self, context):
default_path = os.path.splitext(bpy.data.filepath)[0] + ".psk"
self.layout.operator(ExportUDKAnimData.bl_idname, text="Skeleton Mesh / Animation Data (.psk/.psa)").filepath = default_path
@@ -2661,17 +2660,17 @@ def register():
#print("REGISTER")
bpy.utils.register_module(__name__)
bpy.types.INFO_MT_file_export.append(menu_func)
-
+
def unregister():
#print("UNREGISTER")
bpy.utils.unregister_module(__name__)
bpy.types.INFO_MT_file_export.remove(menu_func)
-
+
if __name__ == "__main__":
#print("\n"*4)
print(header("UDK Export PSK/PSA 2.6", 'CENTER'))
register()
-
+
#loader
#filename = "D:/Projects/BlenderScripts/io_export_udk_psa_psk_alpha.py"
#exec(compile(open(filename).read(), filename, 'exec'))