diff options
-rw-r--r-- | io_export_unreal_psk_psa.py | 2114 |
1 files changed, 1206 insertions, 908 deletions
diff --git a/io_export_unreal_psk_psa.py b/io_export_unreal_psk_psa.py index 7ecef29b..ca22ca46 100644 --- a/io_export_unreal_psk_psa.py +++ b/io_export_unreal_psk_psa.py @@ -1,4 +1,4 @@ -#====================== BEGIN GPL LICENSE BLOCK ============================ +# ##### 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 @@ -14,17 +14,17 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # All rights reserved. # -#======================= END GPL LICENSE BLOCK ============================= +# ##### END GPL LICENSE BLOCK ##### bl_info = { "name": "Export Unreal Engine Format(.psk/.psa)", "author": "Darknet/Optimus_P-Fat/Active_Trash/Sinsoft/VendorX/Spoof", - "version": (2, 7), + "version": (2, 7, 1), "blender": (2, 65, 4), "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": "https://wiki.blender.org/index.php/Extensions:2.6/Py/" "Scripts/Import-Export/Unreal_psk_psa", "category": "Import-Export", } @@ -47,7 +47,8 @@ bl_info = { - 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. +- 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. - It will convert your mesh into triangular when exporting to psk file. - Did not work with psa export yet. @@ -78,7 +79,7 @@ Credit to: """ -#=========================================================================== +# =========================================================================== """ NOTES for Jan 2012 refactor (Spoof) @@ -135,7 +136,7 @@ deformer bones (bad for optimising a game character). I had partial success writing a parser for the structure, but it was taking too much time and, considering the other issues with Rigify, it was abandoned. """ -#=========================================================================== +# =========================================================================== import bmesh import os @@ -143,12 +144,26 @@ import time import bpy import mathutils import math -import random import operator import sys -from bpy.props import * +from bpy.props import ( + BoolProperty, + CollectionProperty, + EnumProperty, + FloatProperty, + IntProperty, + StringProperty, + ) +from bpy.types import ( + Operator, + Panel, + UIList, + PropertyGroup, + AddonPreferences, + ) from struct import pack + # REFERENCE MATERIAL JUST IN CASE: # # U = x / sqrt(x^2 + y^2 + z^2) @@ -157,36 +172,43 @@ from struct import pack # Triangles specifed counter clockwise for front face # # defines for sizeofs -SIZE_FQUAT = 16 -SIZE_FVECTOR = 12 -SIZE_VJOINTPOS = 44 -SIZE_ANIMINFOBINARY = 168 -SIZE_VCHUNKHEADER = 32 -SIZE_VMATERIAL = 88 -SIZE_VBONE = 120 -SIZE_FNAMEDBONEBINARY = 120 -SIZE_VRAWBONEINFLUENCE = 12 -SIZE_VQUATANIMKEY = 32 -SIZE_VVERTEX = 16 -SIZE_VPOINT = 12 -SIZE_VTRIANGLE = 12 - -MaterialName = [] - -#=========================================================================== +SIZE_FQUAT = 16 +SIZE_FVECTOR = 12 +SIZE_VJOINTPOS = 44 +SIZE_ANIMINFOBINARY = 168 +SIZE_VCHUNKHEADER = 32 +SIZE_VMATERIAL = 88 +SIZE_VBONE = 120 +SIZE_FNAMEDBONEBINARY = 120 +SIZE_VRAWBONEINFLUENCE = 12 +SIZE_VQUATANIMKEY = 32 +SIZE_VVERTEX = 16 +SIZE_VPOINT = 12 +SIZE_VTRIANGLE = 12 + +MaterialName = [] + + +# Python 3 doesn't have cmp anymore +def cmp(a, b): + return (a > b) - (a < b) + + +# =========================================================================== # Custom exception class -#=========================================================================== -class Error( Exception ): +# =========================================================================== +class Error(Exception): def __init__(self, message): self.message = message -#=========================================================================== + +# =========================================================================== # Verbose logging with loop truncation -#=========================================================================== -def verbose( msg, iteration=-1, max_iterations=4, msg_truncated="..." ): +# =========================================================================== +def verbose(msg, iteration=-1, max_iterations=4, msg_truncated="..."): - if bpy.context.scene.udk_option_verbose == True: + if bpy.context.scene.udk_option_verbose is True: # limit the number of times a loop can output messages if iteration > max_iterations: return @@ -196,26 +218,28 @@ def verbose( msg, iteration=-1, max_iterations=4, msg_truncated="..." ): print(msg) -#=========================================================================== + +# =========================================================================== # Log header/separator -#=========================================================================== -def header( msg, justify='LEFT', spacer='_', cols=78 ): +# =========================================================================== +def header(msg, justify='LEFT', spacer='_', cols=78): if justify == 'LEFT': - s = '{:{spacer}<{cols}}'.format(msg+" ", spacer=spacer, cols=cols) + s = '{:{spacer}<{cols}}'.format(msg + " ", spacer=spacer, cols=cols) elif justify == 'RIGHT': - s = '{:{spacer}>{cols}}'.format(" "+msg, spacer=spacer, cols=cols) + s = '{:{spacer}>{cols}}'.format(" " + msg, spacer=spacer, cols=cols) else: - s = '{:{spacer}^{cols}}'.format(" "+msg+" ", spacer=spacer, cols=cols) + s = '{:{spacer}^{cols}}'.format(" " + msg + " ", spacer=spacer, cols=cols) return "\n" + s + "\n" -#=========================================================================== + +# =========================================================================== # Generic Object->Integer mapping # the object must be usable as a dictionary key -#=========================================================================== +# =========================================================================== class ObjMap: def __init__(self): @@ -236,11 +260,12 @@ class ObjMap: 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 # 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): @@ -253,10 +278,10 @@ class FQuat: 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) + 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) @@ -264,6 +289,7 @@ class FQuat: def __str__(self): return "[%f,%f,%f,%f](FQuat)" % (self.X, self.Y, self.Z, self.W) + class FVector(object): def __init__(self, X=0.0, Y=0.0, Z=0.0): @@ -275,9 +301,9 @@ class FVector(object): 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) + 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) @@ -303,130 +329,146 @@ class FVector(object): self.Y - other.Y, self.Z - other.Z) + class VJointPos: def __init__(self): - self.Orientation = FQuat() - self.Position = FVector() - self.Length = 0.0 - self.XSize = 0.0 - self.YSize = 0.0 - self.ZSize = 0.0 + self.Orientation = FQuat() + self.Position = FVector() + self.Length = 0.0 + 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) + return self.Orientation.dump() + self.Position.dump() + \ + pack('4f', self.Length, self.XSize, self.YSize, self.ZSize) + class AnimInfoBinary: def __init__(self): - self.Name = "" # length=64 - self.Group = "" # length=64 - self.TotalBones = 0 - self.RootInclude = 0 + self.Name = "" # length=64 + self.Group = "" # length=64 + self.TotalBones = 0 + self.RootInclude = 0 self.KeyCompressionStyle = 0 - self.KeyQuotum = 0 - self.KeyPrediction = 0.0 - self.TrackTime = 0.0 - self.AnimRate = 0.0 - self.StartBone = 0 - self.FirstRawFrame = 0 - self.NumRawFrames = 0 + self.KeyQuotum = 0 + self.KeyPrediction = 0.0 + self.TrackTime = 0.0 + self.AnimRate = 0.0 + 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) + 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) + class VChunkHeader: def __init__(self, name, type_size): - self.ChunkID = str.encode(name) # length=20 - self.TypeFlag = 1999801 # special value - self.DataSize = type_size - self.DataCount = 0 + self.ChunkID = str.encode(name) # length=20 + 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) + class VMaterial: def __init__(self): - self.MaterialName = "" # length=64 - self.TextureIndex = 0 - self.PolyFlags = 0 # DWORD - self.AuxMaterial = 0 - self.AuxFlags = 0 # DWORD - self.LodBias = 0 - self.LodStyle = 0 + self.MaterialName = "" # length=64 + self.TextureIndex = 0 + self.PolyFlags = 0 # DWORD + self.AuxMaterial = 0 + 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) + # print("DATA MATERIAL:",self.MaterialName) + return pack('64siLiLii', str.encode(self.MaterialName), self.TextureIndex, + self.PolyFlags, self.AuxMaterial, self.AuxFlags, self.LodBias, self.LodStyle) + class VBone: def __init__(self): - self.Name = "" # length = 64 - self.Flags = 0 # DWORD - self.NumChildren = 0 - self.ParentIndex = 0 - self.BonePos = VJointPos() + self.Name = "" # length = 64 + self.Flags = 0 # DWORD + 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() + 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): - self.Name = "" # length = 64 - self.Flags = 0 # DWORD - self.NumChildren = 0 - 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 + self.Name = "" # length = 64 + self.Flags = 0 # DWORD + self.NumChildren = 0 + 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() + return pack('64sLii', str.encode(self.Name), self.Flags, + self.NumChildren, self.ParentIndex) + self.BonePos.dump() + class VRawBoneInfluence: def __init__(self): - self.Weight = 0.0 - self.PointIndex = 0 - self.BoneIndex = 0 + self.Weight = 0.0 + self.PointIndex = 0 + self.BoneIndex = 0 def dump(self): return pack('fii', self.Weight, self.PointIndex, self.BoneIndex) + class VQuatAnimKey: def __init__(self): - self.Position = FVector() - self.Orientation = FQuat() - self.Time = 0.0 + self.Position = FVector() + self.Orientation = FQuat() + self.Time = 0.0 def dump(self): return self.Position.dump() + self.Orientation.dump() + pack('f', self.Time) + class VVertex(object): def __init__(self): - self.PointIndex = 0 # WORD - self.U = 0.0 - self.V = 0.0 - self.MatIndex = 0 # BYTE - self.Reserved = 0 # BYTE - self.SmoothGroup = 0 + self.PointIndex = 0 # WORD + self.U = 0.0 + self.V = 0.0 + self.MatIndex = 0 # BYTE + self.Reserved = 0 # BYTE + 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 ) + 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) def _key(self): return (type(self).__name__, self.PointIndex, self.U, self.V, self.MatIndex, self.Reserved) @@ -439,6 +481,7 @@ class VVertex(object): return False return self._key() == other._key() + class VPointSimple: def __init__(self): @@ -458,6 +501,7 @@ class VPointSimple: return False return self._key() == other._key() + class VPoint(object): def __init__(self): @@ -483,33 +527,38 @@ class VPoint(object): return False return self._key() == other._key() + class VTriangle: def __init__(self): - self.WedgeIndex0 = 0 # WORD - self.WedgeIndex1 = 0 # WORD - self.WedgeIndex2 = 0 # WORD - self.MatIndex = 0 # BYTE - self.AuxMatIndex = 0 # BYTE + self.WedgeIndex0 = 0 # WORD + self.WedgeIndex1 = 0 # WORD + self.WedgeIndex2 = 0 # WORD + 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) - #return pack('HHHBBI', self.WedgeIndex0, self.WedgeIndex1, self.WedgeIndex2, self.MatIndex, self.AuxMatIndex, self.SmoothingGroups) - + return pack('HHHBBL', self.WedgeIndex0, self.WedgeIndex1, self.WedgeIndex2, + self.MatIndex, self.AuxMatIndex, self.SmoothingGroups) + """ + print("smooth",self.SmoothingGroups) + return pack('HHHBBI', self.WedgeIndex0, self.WedgeIndex1, self.WedgeIndex2, + self.MatIndex, self.AuxMatIndex, self.SmoothingGroups) + """ # END UNREAL DATA STRUCTS -#=========================================================================== +# =========================================================================== + -#=========================================================================== +# =========================================================================== # 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 + self.Data = [] # list of datatypes def dump(self): data = self.Header.dump() @@ -520,21 +569,22 @@ class FileSection: def UpdateHeader(self): self.Header.DataCount = len(self.Data) -#=========================================================================== + +# =========================================================================== # PSK -#=========================================================================== +# =========================================================================== class PSKFile: def __init__(self): - self.GeneralHeader = VChunkHeader("ACTRHEAD", 0) - self.Points = FileSection("PNTS0000", SIZE_VPOINT) # VPoint - self.Wedges = FileSection("VTXW0000", SIZE_VVERTEX) # VVertex - self.Faces = FileSection("FACE0000", SIZE_VTRIANGLE) # VTriangle - 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 + self.GeneralHeader = VChunkHeader("ACTRHEAD", 0) + self.Points = FileSection("PNTS0000", SIZE_VPOINT) # VPoint + self.Wedges = FileSection("VTXW0000", SIZE_VVERTEX) # VVertex + self.Faces = FileSection("FACE0000", SIZE_VTRIANGLE) # VTriangle + 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 # for vertex groups calculated during the mesh dump, so they can be used again # to dump bone influences during the armature dump # @@ -545,7 +595,7 @@ class PSKFile: # { groupname : [ (index, weight), ... ], ... } # # example: - # { 'MyVertexGroup' : [ (0, 1.0), (5, 1.0), (3, 0.5) ] , 'OtherGroup' : [(2, 1.0)] } + # {'MyVertexGroup' : [ (0, 1.0), (5, 1.0), (3, 0.5) ] , 'OtherGroup' : [(2, 1.0)]} self.VertexGroups = {} @@ -577,7 +627,8 @@ class PSKFile: 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() + 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): @@ -591,14 +642,15 @@ class PSKFile: return m def PrintOut(self): - print( "{:>16} {:}".format( "Points", len(self.Points.Data) ) ) - print( "{:>16} {:}".format( "Wedges", len(self.Wedges.Data) ) ) - print( "{:>16} {:}".format( "Faces", len(self.Faces.Data) ) ) - print( "{:>16} {:}".format( "Materials", len(self.Materials.Data) ) ) - print( "{:>16} {:}".format( "Bones", len(self.Bones.Data) ) ) - print( "{:>16} {:}".format( "Influences", len(self.Influences.Data) ) ) - -#=========================================================================== + print("{:>16} {:}".format("Points", len(self.Points.Data))) + print("{:>16} {:}".format("Wedges", len(self.Wedges.Data))) + print("{:>16} {:}".format("Faces", len(self.Faces.Data))) + print("{:>16} {:}".format("Materials", len(self.Materials.Data))) + print("{:>16} {:}".format("Bones", len(self.Bones.Data))) + print("{:>16} {:}".format("Influences", len(self.Influences.Data))) + + +# =========================================================================== # PSA # # Notes from UDN: @@ -613,14 +665,14 @@ class PSKFile: # 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: def __init__(self): - self.GeneralHeader = VChunkHeader("ANIMHEAD", 0) - self.Bones = FileSection("BONENAMES", SIZE_FNAMEDBONEBINARY) #FNamedBoneBinary - self.Animations = FileSection("ANIMINFO", SIZE_ANIMINFOBINARY) #AnimInfoBinary - self.RawKeys = FileSection("ANIMKEYS", SIZE_VQUATANIMKEY) #VQuatAnimKey + self.GeneralHeader = VChunkHeader("ANIMHEAD", 0) + self.Bones = FileSection("BONENAMES", SIZE_FNAMEDBONEBINARY) # FNamedBoneBinary + self.Animations = FileSection("ANIMINFO", SIZE_ANIMINFOBINARY) # AnimInfoBinary + 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 = {} @@ -656,7 +708,7 @@ class PSAFile: if bone_data[0] == -1: bone_data[0] = len(self.Bones.Data) self.AddBone(bone_data[1]) - #self.Bones.Data.append(bone_data[1]) + # self.Bones.Data.append(bone_data[1]) return bone_data[0] @@ -675,92 +727,100 @@ class PSAFile: 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) ) ) - print( "{:>16} {:}".format( "Raw keys", len(self.RawKeys.Data) ) ) - -#=========================================================================== -# Helpers to create bone structs -#=========================================================================== -def make_vbone( name, parent_index, child_count, orientation_quat, position_vect ): - bone = VBone() - bone.Name = name - bone.ParentIndex = parent_index - bone.NumChildren = child_count - bone.BonePos.Orientation = orientation_quat - bone.BonePos.Position.X = position_vect.x - bone.BonePos.Position.Y = position_vect.y - bone.BonePos.Position.Z = position_vect.z - #these values seem to be ignored? - #bone.BonePos.Length = tail.length - #bone.BonePos.XSize = tail.x - #bone.BonePos.YSize = tail.y - #bone.BonePos.ZSize = tail.z + print("{:>16} {:}".format("Bones", len(self.Bones.Data))) + print("{:>16} {:}".format("Animations", len(self.Animations.Data))) + print("{:>16} {:}".format("Raw keys", len(self.RawKeys.Data))) + + +# =========================================================================== +# Helpers to create bone structs +# =========================================================================== +def make_vbone(name, parent_index, child_count, orientation_quat, position_vect): + bone = VBone() + bone.Name = name + bone.ParentIndex = parent_index + bone.NumChildren = child_count + bone.BonePos.Orientation = orientation_quat + bone.BonePos.Position.X = position_vect.x + bone.BonePos.Position.Y = position_vect.y + bone.BonePos.Position.Z = position_vect.z + # these values seem to be ignored? + # bone.BonePos.Length = tail.length + # bone.BonePos.XSize = tail.x + # bone.BonePos.YSize = tail.y + # bone.BonePos.ZSize = tail.z return bone -def make_namedbonebinary( name, parent_index, child_count, orientation_quat, position_vect, is_real ): - bone = FNamedBoneBinary() - bone.Name = name - bone.ParentIndex = parent_index - bone.NumChildren = child_count - bone.BonePos.Orientation = orientation_quat - bone.BonePos.Position.X = position_vect.x - bone.BonePos.Position.Y = position_vect.y - bone.BonePos.Position.Z = position_vect.z - bone.IsRealBone = is_real + +def make_namedbonebinary(name, parent_index, child_count, orientation_quat, position_vect, is_real): + bone = FNamedBoneBinary() + bone.Name = name + bone.ParentIndex = parent_index + bone.NumChildren = child_count + bone.BonePos.Orientation = orientation_quat + bone.BonePos.Position.X = position_vect.x + bone.BonePos.Position.Y = position_vect.y + bone.BonePos.Position.Z = position_vect.z + bone.IsRealBone = is_real return bone -def make_fquat( bquat ): - quat = FQuat() - #flip handedness for UT = set x,y,z to negative (rotate in other direction) - quat.X = -bquat.x - quat.Y = -bquat.y - quat.Z = -bquat.z - quat.W = bquat.w + +def make_fquat(bquat): + quat = FQuat() + # flip handedness for UT = set x,y,z to negative (rotate in other direction) + quat.X = -bquat.x + quat.Y = -bquat.y + quat.Z = -bquat.z + quat.W = bquat.w + return quat -def make_fquat_default( bquat ): - quat = FQuat() - #print(dir(bquat)) - quat.X = bquat.x - quat.Y = bquat.y - quat.Z = bquat.z - quat.W = bquat.w + +def make_fquat_default(bquat): + quat = FQuat() + # print(dir(bquat)) + quat.X = bquat.x + quat.Y = bquat.y + quat.Z = bquat.z + quat.W = bquat.w + return quat -#=========================================================================== -#RG - check to make sure face isnt a line -#=========================================================================== -def is_1d_face( face, mesh ): - #ID Vertex of id point + +# =========================================================================== +# RG - check to make sure face isnt a line +# =========================================================================== +def is_1d_face(face, mesh): + # ID Vertex of id point 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) + 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) return False -#=========================================================================== + +# =========================================================================== # Smoothing group # (renamed to seperate it from VVertex.SmoothGroup) -#=========================================================================== +# =========================================================================== class SmoothingGroup: static_id = 1 def __init__(self): - self.faces = [] - self.neighboring_faces = [] + self.faces = [] + self.neighboring_faces = [] self.neighboring_groups = [] - self.id = -1 - self.local_id = SmoothingGroup.static_id + 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 cmp(self.local_id, other.local_id) return -1 def __hash__(self): @@ -774,27 +834,29 @@ class SmoothingGroup: if temp_id < 0x80000000: temp_id = temp_id << 1 else: - raise Error("Smoothing Group ID Overflowed, Smoothing Group evidently has more than 31 neighboring groups") + 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 ) + 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 ) + if face not in self.neighboring_faces: + self.neighboring_faces.append(face) def add_face(self, face): - if not face in self.faces: - self.faces.append( face ) + if face not in self.faces: + self.faces.append(face) -def determine_edge_sharing( mesh ): + +def determine_edge_sharing(mesh): edge_sharing_list = dict() @@ -803,12 +865,13 @@ def determine_edge_sharing( mesh ): 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 + if face not 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 ): + +def find_edges(mesh, key): """ Temp replacement for mesh.findEdges(). This is painfully slow. """ @@ -817,7 +880,8 @@ def find_edges( mesh, key ): if key[0] == v[0] and key[1] == v[1]: return edge.index -def add_face_to_smoothgroup( mesh, face, edge_sharing_list, smoothgroup ): + +def add_face_to_smoothgroup(mesh, face, edge_sharing_list, smoothgroup): if face in smoothgroup.faces: return @@ -831,62 +895,68 @@ def add_face_to_smoothgroup( mesh, face, edge_sharing_list, smoothgroup ): if edge_id is not None: # not sharp - if not( mesh.edges[edge_id].use_edge_sharp): + if not (mesh.edges[edge_id].use_edge_sharp): for shared_face in edge_sharing_list[key]: if shared_face != face: # recursive - add_face_to_smoothgroup( mesh, shared_face, edge_sharing_list, smoothgroup ) + add_face_to_smoothgroup(mesh, shared_face, edge_sharing_list, smoothgroup) # sharp else: for shared_face in edge_sharing_list[key]: if shared_face != face: - smoothgroup.add_neighbor_face( shared_face ) + smoothgroup.add_neighbor_face(shared_face) + -def determine_smoothgroup_for_face( mesh, face, edge_sharing_list, smoothgroup_list ): +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 ) + smoothgroup = SmoothingGroup() + add_face_to_smoothgroup(mesh, face, edge_sharing_list, smoothgroup) + + if smoothgroup not in smoothgroup_list: + smoothgroup_list.append(smoothgroup) - if not smoothgroup in smoothgroup_list: - smoothgroup_list.append( smoothgroup ) -def build_neighbors_tree( smoothgroup_list ): +def build_neighbors_tree(smoothgroup_list): for group in smoothgroup_list: for face in group.neighboring_faces: for neighbor_group in smoothgroup_list: - if neighbor_group.contains_face( face ) and neighbor_group not in group.neighboring_groups: - group.make_neighbor( neighbor_group ) - neighbor_group.make_neighbor( group ) + if neighbor_group.contains_face(face) and neighbor_group not in group.neighboring_groups: + group.make_neighbor(neighbor_group) + neighbor_group.make_neighbor(group) -#=========================================================================== + +# =========================================================================== # parse_smooth_groups -#=========================================================================== -def parse_smooth_groups( mesh ): +# =========================================================================== +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) - #print("FACES:",len(mesh.tessfaces),"//100 =" "interval:",interval) + 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) + # print("FACES:",len(mesh.tessfaces),"//100 =" "interval:",interval) + for face in mesh.tessfaces: - #print(dir(face)) + # print(dir(face)) determine_smoothgroup_for_face(mesh, face, edge_sharing_list, smoothgroup_list) # progress indicator, writes to console without scrolling if face.index > 0 and (face.index % interval) == 0: - print("Processing... {}%\r".format( int(face.index / len(mesh.tessfaces) * 100) ), end='') + print("Processing... {}%\r".format(int(face.index / len(mesh.tessfaces) * 100)), end='') sys.stdout.flush() - print("Completed" , ' '*20) + print("Completed", ' ' * 20) verbose("len(smoothgroup_list)={}".format(len(smoothgroup_list))) @@ -898,31 +968,33 @@ def parse_smooth_groups( mesh ): print("Smooth group parsing completed in {:.2f}s".format(time.clock() - t)) return smoothgroup_list -#=========================================================================== + +# =========================================================================== # http://en.wikibooks.org/wiki/Blender_3D:_Blending_Into_Python/Cookbook#Triangulate_NMesh # blender 2.50 format using the Operators/command convert the mesh to tri mesh -#=========================================================================== -def triangulate_mesh( object ): +# =========================================================================== +def triangulate_mesh(object): verbose(header("triangulateNMesh")) - #print(type(object)) + # 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 + 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) bpy.context.scene.update() bpy.ops.object.mode_set(mode='OBJECT') + for i in scene.objects: - i.select = False # deselect all objects + i.select = False # deselect all objects - me_ob.select = True - scene.objects.active = me_ob + 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.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() @@ -933,81 +1005,99 @@ def triangulate_mesh( object ): verbose("Triangulated mesh") - me_ob.data = me_ob.to_mesh(bpy.context.scene, True, 'PREVIEW') #write data object + me_ob.data = me_ob.to_mesh(bpy.context.scene, True, 'PREVIEW') # write data object bpy.context.scene.update() return me_ob -#copy mesh data and then merge them into one object + +# copy mesh data and then merge them into one object def meshmerge(selectedobjects): - bpy.ops.object.mode_set(mode='OBJECT') #object mode and not edit mode - cloneobjects = [] #object holder for copying object data + bpy.ops.object.mode_set(mode='OBJECT') # object mode and not edit mode + cloneobjects = [] # object holder for copying object data + if len(selectedobjects) > 1: - print("selectedobjects:",len(selectedobjects)) #print select object - count = 0 #reset count - for count in range(len( selectedobjects)): - #print("Index:",count) + print("selectedobjects:", len(selectedobjects)) # print select object + count = 0 # reset count + + for count in range(len(selectedobjects)): + # print("Index:",count) if selectedobjects[count] is not None: - me_da = selectedobjects[count].data.copy() #copy data - me_ob = selectedobjects[count].copy() #copy object - #note two copy two types else it will use the current data or mesh - me_ob.data = me_da #assign the data - bpy.context.scene.objects.link(me_ob)#link the object to the scene #current object location - print("Index:",count,"clone object",me_ob.name) #print clone object - cloneobjects.append(me_ob) #add object to the array - for i in bpy.data.objects: i.select = False #deselect all objects - count = 0 #reset count - #begin merging the mesh together as one - for count in range(len( cloneobjects)): + me_da = selectedobjects[count].data.copy() # copy data + me_ob = selectedobjects[count].copy() # copy object + # note two copy two types else it will use the current data or mesh + me_ob.data = me_da # assign the data + bpy.context.scene.objects.link(me_ob) # link the object to the scene #current object location + print("Index:", count, "clone object", me_ob.name) # print clone object + cloneobjects.append(me_ob) # add object to the array + + for i in bpy.data.objects: + i.select = False # deselect all objects + count = 0 # reset count + # begin merging the mesh together as one + for count in range(len(cloneobjects)): if count == 0: bpy.context.scene.objects.active = cloneobjects[count] - print("Set Active Object:",cloneobjects[count].name) + print("Set Active Object:", cloneobjects[count].name) cloneobjects[count].select = True - bpy.ops.object.join() #join object together + bpy.ops.object.join() # join object together 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. + +# 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...") centermesh = [] notcentermesh = [] for countm in range(len(selectmesh)): - #if object are center add here - if selectmesh[countm].location.x == 0 and selectmesh[countm].location.y == 0 and selectmesh[countm].location.z == 0: + # if object are center add here + if selectmesh[countm].location.x == 0 and \ + selectmesh[countm].location.y == 0 and \ + selectmesh[countm].location.z == 0: centermesh.append(selectmesh[countm]) - else:#if not add here for not center + 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)): selectmesh.append(notcentermesh[countm]) - if len(selectmesh) == 1: #if there one mesh just do some here - return selectmesh[0] #return object mesh + if len(selectmesh) == 1: # if there one mesh just do some here + return selectmesh[0] # return object mesh else: - return meshmerge(selectmesh) #return merge object mesh + return meshmerge(selectmesh) # return merge object mesh + + import binascii -#=========================================================================== + + +# =========================================================================== # parse_mesh -#=========================================================================== -def parse_mesh( mesh, psk ): - #bpy.ops.object.mode_set(mode='OBJECT') - #error ? on commands for select object? +# =========================================================================== +def parse_mesh(mesh, psk): + # bpy.ops.object.mode_set(mode='OBJECT') + # error ? on commands for select object? print(header("MESH", 'RIGHT')) print("Mesh object:", mesh.name) scene = bpy.context.scene - for i in scene.objects: i.select = False # deselect all objects - scene.objects.active = mesh + + for i in scene.objects: + i.select = False # deselect all objects + + scene.objects.active = mesh setmesh = mesh mesh = triangulate_mesh(mesh) - if bpy.types.Scene.udk_copy_merge == True: + + if bpy.types.Scene.udk_copy_merge is True: bpy.context.scene.objects.unlink(setmesh) - #print("FACES----:",len(mesh.data.tessfaces)) + + # print("FACES----:",len(mesh.data.tessfaces)) verbose("Working mesh object: {}".format(mesh.name)) - #collect a list of the material names + # collect a list of the material names print("Materials...") mat_slot_index = 0 @@ -1016,26 +1106,30 @@ def parse_mesh( mesh, psk ): print(" Material {} '{}'".format(mat_slot_index, slot.name)) MaterialName.append(slot.name) - #if slot.material.texture_slots[0] is not None: - #if slot.material.texture_slots[0].texture.image.filepath is not None: - #print(" Texture path {}".format(slot.material.texture_slots[0].texture.image.filepath)) - #create the current material - v_material = psk.GetMatByIndex(mat_slot_index) + + """ + if slot.material.texture_slots[0] is not None: + if slot.material.texture_slots[0].texture.image.filepath is not None: + 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 v_material.TextureIndex = mat_slot_index - v_material.AuxMaterial = mat_slot_index + v_material.AuxMaterial = mat_slot_index mat_slot_index += 1 verbose(" PSK index {}".format(v_material.TextureIndex)) - #END slot in mesh.material_slots + # 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 + # object_material_index = mesh.active_material_index + # FIXME ^ this is redundant due to "= face.material_index" in face loop - wedges = ObjMap() - points = ObjMap() #vertex - points_linked = {} + wedges = ObjMap() + points = ObjMap() # vertex + points_linked = {} discarded_face_count = 0 sys.setrecursionlimit(1000000) @@ -1054,15 +1148,13 @@ def parse_mesh( mesh, psk ): smoothgroup_id = smooth_group.id break - #print ' -- Dumping UVs -- ' - #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 @@ -1073,40 +1165,40 @@ def parse_mesh( mesh, psk ): if not is_1d_face(face, mesh.data): - wedge_list = [] - vect_list = [] + wedge_list = [] + vect_list = [] - #get or create the current material + # get or create the current material psk.GetMatByIndex(object_material_index) - face_index = face.index - has_uv = False - face_uv = None + face_index = face.index + has_uv = False + face_uv = None if len(mesh.data.uv_textures) > 0: - 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])) + 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] - uv = [] - #assumes 3 UVs Per face (for now) + vert_index = face.vertices[i] + vert = mesh.data.vertices[vert_index] + uv = [] + # assumes 3 UVs Per face (for now) if (has_uv): if len(face_uv.uv) != 3: print("WARNING: face has more or less than 3 UV coordinates - writing 0,0...") uv = [0.0, 0.0] else: - uv = [face_uv.uv[i][0],face_uv.uv[i][1]] #OR bottom works better # 24 for cube + uv = [face_uv.uv[i][0], face_uv.uv[i][1]] # OR bottom works better # 24 for cube else: - #print ("No UVs?") + # 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 + # 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 @@ -1122,31 +1214,32 @@ def parse_mesh( mesh, psk ): # 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) ) + vect_list.append(FVector(vert.co.x, vert.co.y, vert.co.z)) # Transform position for export - #vpos = vert.co * object_material_index + # vpos = vert.co * object_material_index - #should fixed this!! + # should fixed this!! vpos = mesh.matrix_local * vert.co if bpy.context.scene.udk_option_scale < 0 or bpy.context.scene.udk_option_scale > 1: - #print("OK!") + # print("OK!") vpos.x = vpos.x * bpy.context.scene.udk_option_scale vpos.y = vpos.y * bpy.context.scene.udk_option_scale vpos.z = vpos.z * bpy.context.scene.udk_option_scale - #print("scale pos:", vpos) + + # print("scale pos:", vpos) # Create the point - p = VPoint() - p.Point.X = vpos.x - p.Point.Y = vpos.y - p.Point.Z = vpos.z - if bpy.context.scene.udk_option_smoothing_groups:#is this necessary? + p = VPoint() + p.Point.X = vpos.x + p.Point.Y = vpos.y + p.Point.Z = vpos.z + if bpy.context.scene.udk_option_smoothing_groups: # is this necessary? p.SmoothGroup = smoothgroup_id - lPoint = VPointSimple() - lPoint.Point.X = vpos.x - lPoint.Point.Y = vpos.y - lPoint.Point.Z = vpos.z + lPoint = VPointSimple() + 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]): @@ -1155,24 +1248,24 @@ def parse_mesh( mesh, psk ): points_linked[lPoint] = [p] # Create the wedge - w = VVertex() - w.MatIndex = object_material_index - w.PointIndex = points.get(p) # store keys - w.U = uv[0] - w.V = uv[1] - if bpy.context.scene.udk_option_smoothing_groups:#is this necessary? + w = VVertex() + w.MatIndex = object_material_index + w.PointIndex = points.get(p) # store keys + w.U = uv[0] + w.V = uv[1] + if bpy.context.scene.udk_option_smoothing_groups: # is this necessary? 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( + # 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) + # END for i in range(3) # Determine face vertex order @@ -1198,9 +1291,9 @@ def parse_mesh( mesh, psk ): elif dot < 0: (tri.WedgeIndex0, tri.WedgeIndex1, tri.WedgeIndex2) = wedge_list else: - dindex0 = face.vertices[0]; - dindex1 = face.vertices[1]; - dindex2 = face.vertices[2]; + dindex0 = face.vertices[0] + dindex1 = face.vertices[1] + dindex2 = face.vertices[2] mesh.data.vertices[dindex0].select = True mesh.data.vertices[dindex1].select = True @@ -1211,7 +1304,7 @@ def parse_mesh( mesh, psk ): str(mesh.data.vertices[dindex2].co))) face.select = True - if face.use_smooth == True: + if face.use_smooth is True: tri.SmoothingGroups = 1 else: tri.SmoothingGroups = 0 @@ -1223,12 +1316,12 @@ def parse_mesh( mesh, psk ): 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 + # END face in mesh.data.faces print("{} points".format(len(points.dict))) @@ -1236,7 +1329,7 @@ def parse_mesh( mesh, psk ): psk.AddPoint(point) if len(points.dict) > 32767: - raise Error("Mesh vertex limit exceeded! {} > 32767".format(len(points.dict))) + raise Error("Mesh vertex limit exceeded! {} > 32767".format(len(points.dict))) print("{} wedges".format(len(wedges.dict))) @@ -1248,73 +1341,75 @@ def parse_mesh( mesh, psk ): 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 - #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 + # 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("pointsindex length ",len(points_linked)) - #vertex group + # [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)) + # 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)) + # print(dir(vertex)) # all groups this vertex is a member of... for vgroup in vertex.groups: if vgroup.group == obj_vertex_group.index: - vertex_weight = vgroup.weight - p = VPointSimple() - vpos = mesh.matrix_local * vertex.co + vertex_weight = vgroup.weight + p = VPointSimple() + vpos = mesh.matrix_local * vertex.co + if bpy.context.scene.udk_option_scale < 0 or bpy.context.scene.udk_option_scale > 1: vpos.x = vpos.x * bpy.context.scene.udk_option_scale 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.Z = vpos.z - #print(p) - #print(len(points_linked[p])) - try: #check if point doesn't give error + p.Point.X = vpos.x + p.Point.Y = vpos.y + p.Point.Z = vpos.z + # print(p) + # print(len(points_linked[p])) + try: # check if point doesn't give error for point in points_linked[p]: - point_index = points.get(point) #point index - v_item = (point_index, vertex_weight) + point_index = points.get(point) # point index + v_item = (point_index, vertex_weight) vertex_list.append(v_item) - except Exception:#if get error ignore them #not safe I think + 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)) + # 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: + if bpy.context.scene.udk_option_triangulate is True: verbose("Removing temporary triangle mesh: {}".format(mesh.name)) bpy.ops.object.mode_set(mode='OBJECT') # OBJECT mode mesh.parent = None # unparent to avoid phantom links bpy.context.scene.objects.unlink(mesh) # unlink -#=========================================================================== + +# =========================================================================== # Collate bones that belong to the UDK skeletal mesh -#=========================================================================== -def parse_armature( armature, psk, psa ): +# =========================================================================== +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 is None and b.use_deform == True] + root_candidates = [b for b in armature.data.bones if b.parent is None and b.use_deform is True] # should be a single, unambiguous result if len(root_candidates) == 0: @@ -1324,9 +1419,9 @@ def parse_armature( armature, psk, psa ): 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 + 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")) @@ -1341,7 +1436,8 @@ def parse_armature( armature, psk, psa ): # this is passed to parse_animation instead of working from keyed bones in the action return udk_bones -#=========================================================================== + +# =========================================================================== # bone current bone # bones bone list # psk the PSK file object @@ -1349,19 +1445,18 @@ def parse_armature( armature, psk, psa ): # parent_id # parent_matrix # indent text indent for recursive log -#=========================================================================== -def recurse_bone( bone, bones, psk, psa, parent_id, parent_matrix, indent="" ): +# =========================================================================== +def recurse_bone(bone, bones, psk, psa, parent_id, parent_matrix, indent=""): status = "Ok" - - bones.append(bone); + bones.append(bone) if not bone.use_deform: status = "No effect" # calc parented bone transform if bone.parent is not None: - quat = make_fquat(bone.matrix.to_quaternion()) + quat = make_fquat(bone.matrix.to_quaternion()) quat_parent = bone.parent.matrix.to_quaternion().inverted() parent_head = quat_parent * bone.parent.head parent_tail = quat_parent * bone.parent.tail @@ -1370,73 +1465,81 @@ def recurse_bone( bone, bones, psk, psa, parent_id, parent_matrix, indent="" ): # calc root bone transform else: translation = parent_matrix * bone.head # ARMATURE OBJECT Location - rot_matrix = bone.matrix * parent_matrix.to_3x3() # ARMATURE OBJECT Rotation - quat = make_fquat_default(rot_matrix.to_quaternion()) - #udk_option_scale bones here? + rot_matrix = bone.matrix * parent_matrix.to_3x3() # ARMATURE OBJECT Rotation + quat = make_fquat_default(rot_matrix.to_quaternion()) + + # udk_option_scale bones here? if bpy.context.scene.udk_option_scale < 0 or bpy.context.scene.udk_option_scale > 1: translation.x = translation.x * bpy.context.scene.udk_option_scale translation.y = translation.y * bpy.context.scene.udk_option_scale translation.z = translation.z * bpy.context.scene.udk_option_scale - bone_id = BoneUtil.static_bone_id # ALT VERS + 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) ) + 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 + # 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] - #print("vertex list:", len(vertex_list), " of >" ,bone.name ) + # print("vertex list:", len(vertex_list), " of >" ,bone.name) + for vertex_data in vertex_list: - point_index = vertex_data[0] - vertex_weight = vertex_data[1] - influence = VRawBoneInfluence() - influence.Weight = vertex_weight - influence.BoneIndex = bone_id - influence.PointIndex = point_index - #print (" AddInfluence to vertex {}, weight={},".format(point_index, vertex_weight)) + point_index = vertex_data[0] + vertex_weight = vertex_data[1] + influence = VRawBoneInfluence() + influence.Weight = vertex_weight + influence.BoneIndex = bone_id + influence.PointIndex = point_index + # print (" AddInfluence to vertex {}, weight={},".format(point_index, vertex_weight)) psk.AddInfluence(influence) else: status = "No vertex group" - #FIXME overwriting previous status error? + # FIXME overwriting previous status error? - print("{:<3} {:<48} {:<20}".format(bone_id, indent+bone.name, status)) + print("{:<3} {:<48} {:<20}".format(bone_id, indent + bone.name, status)) - #bone.matrix_local - #recursively dump child bones + # 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) + recurse_bone(child_bone, bones, psk, psa, bone_id, parent_matrix, " " + indent) + # FIXME rename? remove? class BoneUtil: - static_bone_id = 0 # static property to replace global + static_bone_id = 0 # static property to replace global -#=========================================================================== + +# =========================================================================== # armature the armature # udk_bones list of bones to be exported # actions_to_export list of actions to process for export # psa the PSA file object -#=========================================================================== -def parse_animation( armature, udk_bones, actions_to_export, psa ): +# =========================================================================== +def parse_animation(armature, udk_bones, actions_to_export, psa): print(header("ANIMATION", 'RIGHT')) - context = bpy.context - anim_rate = context.scene.render.fps + 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))) - print() - if armature.animation_data is None: #if animation data was not create for the armature it will skip the exporting action set(s) + print("Scene: {} FPS: {} Frames: {} to {}".format(context.scene.name, anim_rate, + context.scene.frame_start, context.scene.frame_end) + ) + print("Processing {} action(s)\n".format(len(actions_to_export))) + + # if animation data was not create for the armature it will skip the exporting action set(s) + if armature.animation_data is None: 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 + restoreAction = armature.animation_data.action # Q: is animation_data always valid? + # we already do this in export_proxy, but we'll do it here too for now + restoreFrame = context.scene.frame_current raw_frame_index = 0 # used to set FirstRawFrame, seperating actions in the raw keyframe array # action loop... @@ -1449,24 +1552,24 @@ def parse_animation( armature, udk_bones, actions_to_export, psa ): continue # apply action to armature and update scene - # note if loop all actions that is not armature it will override and will break armature animation. + # 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) + 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 - anim.Group = "" # unused? - anim.NumRawFrames = frame_count - anim.AnimRate = anim_rate - anim.FirstRawFrame = raw_frame_index + anim = AnimInfoBinary() + anim.Name = action.name + anim.Group = "" # unused? + 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)) @@ -1479,7 +1582,7 @@ def parse_animation( armature, udk_bones, actions_to_export, psa ): for pb in armature.pose.bones: if b.name == pb.name: udk_pose_bones.append(pb) - break; + break # sort in the order the bones appear in the PSA file ordered_bones = {} @@ -1493,7 +1596,7 @@ def parse_animation( armature, udk_bones, actions_to_export, psa ): frame = scene_range[i] - #verbose("FRAME {}".format(i), i) # test loop sampling + # verbose("FRAME {}".format(i), i) # test loop sampling # advance to frame (automatically updates the pose) context.scene.frame_set(frame) @@ -1501,36 +1604,36 @@ def parse_animation( armature, udk_bones, actions_to_export, psa ): # 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) + bone_index = bone_data[0] + pose_bone = bone_data[1] + pose_bone_matrix = mathutils.Matrix(pose_bone.matrix) if pose_bone.parent is not None: pose_bone_parent_matrix = mathutils.Matrix(pose_bone.parent.matrix) - pose_bone_matrix = pose_bone_parent_matrix.inverted() * pose_bone_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() + head = pose_bone_matrix.to_translation() + quat = pose_bone_matrix.to_quaternion().normalized() if pose_bone.parent is not None: quat = make_fquat(quat) else: quat = make_fquat_default(quat) - #scale animation position here? + # 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 head.y = head.y * bpy.context.scene.udk_option_scale head.z = head.z * bpy.context.scene.udk_option_scale - vkey = VQuatAnimKey() - vkey.Position.X = head.x - vkey.Position.Y = head.y - vkey.Position.Z = head.z - vkey.Orientation = quat + vkey = VQuatAnimKey() + vkey.Position.X = head.x + vkey.Position.Y = head.y + vkey.Position.Z = head.z + vkey.Orientation = quat # frame delta = 1.0 / fps - vkey.Time = 1.0 / anim_rate # according to C++ header this is "disregarded" + vkey.Time = 1.0 / anim_rate # according to C++ header this is "disregarded" psa.AddRawKey(vkey) @@ -1540,8 +1643,10 @@ def parse_animation( armature, udk_bones, actions_to_export, psa ): # 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 + # REMOVED len(unique_bone_indexes) + anim.TotalBones = len(ordered_bones) + # frame_count/anim.AnimRate makes more sense, but this is what actually works in UDK + anim.TrackTime = float(frame_count) verbose("anim.TotalBones={}, anim.TrackTime={}".format(anim.TotalBones, anim.TrackTime)) @@ -1553,52 +1658,54 @@ def parse_animation( armature, udk_bones, actions_to_export, psa ): armature.animation_data.action = restoreAction context.scene.frame_set(restoreFrame) -#=========================================================================== + +# =========================================================================== # Collate actions to be exported # Modify this to filter for one, some or all actions. For now use all. # RETURNS list of actions -#=========================================================================== +# =========================================================================== 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 + if bpy.context.scene.udk_option_selectanimations: # check if needed to select actions set for exporting it print("Action Set is selected!") bready = False - for actionlist in bpy.context.scene.udkas_list: #list the action set from the list - if actionlist.name == action.name and actionlist.bmatch == True and actionlist.bexport == True: + for actionlist in bpy.context.scene.udkas_list: # list the action set from the list + if actionlist.name == action.name and actionlist.bmatch is True and actionlist.bexport is True: bready = True - print("Added Action Set:",action.name) + print("Added Action Set:", action.name) break - if bready == False:#don't export it - print("Skipping Action Set:",action.name) + if bready is False: # don't export it + print("Skipping Action Set:", action.name) continue - verbose(" + {}".format(action.name)) #action set name - actions_to_export.append(action) #add to the action array + verbose(" + {}".format(action.name)) # action set name + actions_to_export.append(action) # add to the action array return actions_to_export -#=========================================================================== + +# =========================================================================== # Locate the target armature and mesh for export # RETURNS armature, mesh -#=========================================================================== +# =========================================================================== 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 + 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') + # bpy.ops.object.mode_set(mode='OBJECT') - if bpy.context.scene.udk_option_selectobjects: #if checked select object true do list object on export + 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: - print("Armature Name:",bpy.context.scene.udkArm_list[bpy.context.scene.udkArm_list_idx].name) + print("Armature Name:", bpy.context.scene.udkArm_list[bpy.context.scene.udkArm_list_idx].name) for obj in bpy.context.scene.objects: if obj.name == bpy.context.scene.udkArm_list[bpy.context.scene.udkArm_list_idx].name: armature = obj @@ -1606,21 +1713,22 @@ def find_armature_and_mesh(): else: raise Error("There is no Armature in the list!") meshselected = [] - #parented_meshes = [obj for obj in armature.children if obj.type == 'MESH'] + # parented_meshes = [obj for obj in armature.children if obj.type == 'MESH'] meshes = [obj for obj in bpy.context.scene.objects if obj.type == 'MESH'] for obj in meshes: - #print(dir(obj)) + # print(dir(obj)) if obj.type == 'MESH': bexportmesh = False - #print("PARENT MESH:",obj.name) + # print("PARENT MESH:",obj.name) for udkmeshlist in bpy.context.scene.udkmesh_list: - if obj.name == udkmeshlist.name and udkmeshlist.bexport == True: + if obj.name == udkmeshlist.name and udkmeshlist.bexport is True: bexportmesh = True break - if bexportmesh == True: - print("Mesh Name:",obj.name," < SELECT TO EXPORT!") + if bexportmesh is True: + print("Mesh Name:", obj.name, " < SELECT TO EXPORT!") meshselected.append(obj) - print("MESH COUNT:",len(meshselected)) + + print("MESH COUNT:", len(meshselected)) # try the active object if active_object and active_object.type == 'MESH' and len(meshselected) == 0: if active_object.parent == armature: @@ -1630,8 +1738,8 @@ def find_armature_and_mesh(): # 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)) + print("Number of meshes:", len(meshes)) + print("Number of meshes (selected):", len(meshes)) if len(meshes) == 1: mesh = meshes[0] @@ -1642,7 +1750,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") - else: #if not check for select function from the list work the code here + else: # if not check for select function from the list work the code here print("normal mode:") # try the active object if active_object and active_object.type == 'ARMATURE': @@ -1650,19 +1758,19 @@ def find_armature_and_mesh(): bpy.ops.object.mode_set(mode='OBJECT') # otherwise, try for a single armature in the scene else: - #bpy.ops.object.mode_set(mode='OBJECT') + # 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 + 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 + elif len(all_armatures) > 1: # if there more armature then find the select armature barmselect = False for _armobj in all_armatures: if _armobj.select: armature = _armobj barmselect = True break - if barmselect == False: + if barmselect is False: raise Error("Please select an armatures in the scene") else: raise Error("No armatures in scene") @@ -1676,8 +1784,8 @@ def find_armature_and_mesh(): 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: + # print(dir(obj)) + if obj.type == 'MESH' and obj.select is True: meshselected.append(obj) # try the active object if active_object and active_object.type == 'MESH' and len(meshselected) == 0: @@ -1688,8 +1796,8 @@ def find_armature_and_mesh(): # 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)) + print("Number of meshes:", len(parented_meshes)) + print("Number of meshes (selected):", len(meshselected)) if len(parented_meshes) == 1: mesh = parented_meshes[0] @@ -1704,28 +1812,33 @@ def find_armature_and_mesh(): verbose("Found mesh: {}".format(mesh.name)) if mesh is None or armature is None: raise Error("Check Mesh and Armature are list!") - #if len(armature.pose.bones) == len(mesh.vertex_groups): - #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 len(armature.pose.bones) == len(mesh.vertex_groups): + 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...") + # print("INIT... REBUILDING...") print("REBUILDING ARMATURE...") - #if deform mesh - armature = rebuildarmature(armature) #rebuild the armature to raw . If there IK constraint it will ignore it. + # if deform mesh + # rebuild the armature to raw. If there IK constraint it will ignore it + armature = rebuildarmature(armature) print("REBUILDING MESH...") - mesh = rebuildmesh(mesh) #rebuild the mesh to raw data format. + mesh = rebuildmesh(mesh) # rebuild the mesh to raw data format. return armature, mesh -#=========================================================================== + +# =========================================================================== # Returns a list of vertex groups in the mesh. Can be modified to filter # groups as necessary. # UNUSED -#=========================================================================== -def collate_vertex_groups( mesh ): +# =========================================================================== +def collate_vertex_groups(mesh): verbose("collate_vertex_groups") groups = [] @@ -1736,13 +1849,14 @@ def collate_vertex_groups( mesh ): return groups -#=========================================================================== + +# =========================================================================== # Main -#=========================================================================== +# =========================================================================== def export(filepath): print(header("Export", 'RIGHT')) - bpy.types.Scene.udk_copy_merge = False #in case fail to export set this to default - t = time.clock() + 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])) @@ -1778,7 +1892,7 @@ def export(filepath): udk_bones = parse_armature(udk_armature, psk, psa) # step 3 - if context.scene.udk_option_export_psa == True: + if context.scene.udk_option_export_psa is True: actions = collate_actions() parse_animation(udk_armature, udk_bones, actions, psa) @@ -1788,7 +1902,7 @@ def export(filepath): psk_filename = filepath + '.psk' psa_filename = filepath + '.psa' - if context.scene.udk_option_export_psk == True: + if context.scene.udk_option_export_psk is True: print("Skeletal mesh data...") psk.PrintOut() file = open(psk_filename, "wb") @@ -1797,7 +1911,7 @@ def export(filepath): print("Exported: " + psk_filename) print() - if context.scene.udk_option_export_psa == True: + if context.scene.udk_option_export_psa is True: print("Animation data...") if not psa.IsEmpty(): psa.PrintOut() @@ -1810,27 +1924,27 @@ def export(filepath): print() - #if objects are rebuild do the unlink + # if objects are rebuild do the unlink if bpy.context.scene.udk_option_rebuildobjects: print("Unlinking Objects") - print("Armature Object Name:",udk_armature.name) #display object name - bpy.context.scene.objects.unlink(udk_armature) #remove armature from the scene - print("Mesh Object Name:",udk_mesh.name) #display object name - bpy.context.scene.objects.unlink(udk_mesh) #remove mesh from the scene + print("Armature Object Name:", udk_armature.name) # display object name + bpy.context.scene.objects.unlink(udk_armature) # remove armature from the scene + print("Mesh Object Name:", udk_mesh.name) # display object name + bpy.context.scene.objects.unlink(udk_mesh) # remove mesh from the scene print("Export completed in {:.2f} seconds".format((time.clock() - t))) -#=========================================================================== + +# =========================================================================== # Operator -#=========================================================================== -class Operator_UDKExport( bpy.types.Operator ): +# =========================================================================== +class Operator_UDKExport(Operator): """Export to UDK""" - bl_idname = "object.udk_export" - bl_label = "Export now" + bl_idname = "object.udk_export" + bl_label = "Export now" def execute(self, context): - print( "\n"*8 ) - + print("\n" * 8) scene = bpy.context.scene scene.udk_option_export_psk = (scene.udk_option_export == '0' or scene.udk_option_export == '2') @@ -1840,8 +1954,7 @@ class Operator_UDKExport( bpy.types.Operator ): # cache settings restore_frame = scene.frame_current - - message = "Finish Export!" + message = "Object(s) exported to: {}".format(filepath) try: export(filepath) @@ -1852,144 +1965,80 @@ class Operator_UDKExport( bpy.types.Operator ): # restore settings scene.frame_set(restore_frame) - self.report({'ERROR'}, message) + def draw(self, context): + self.layout.label(text="Export Finished") + try: + context.window_manager.popup_menu(draw, title=message, icon="INFO") + except: + pass + + self.report({'INFO'}, message) # restore settings scene.frame_set(restore_frame) return {'FINISHED'} -#=========================================================================== + +# =========================================================================== # Operator -#=========================================================================== -class Operator_ToggleConsole( bpy.types.Operator ): +# =========================================================================== +class Operator_ToggleConsole(Operator): """Show or hide the console""" - bl_idname = "object.toggle_console" - bl_label = "Toggle console" + bl_idname = "object.toggle_console" + bl_label = "Toggle console" - #def invoke(self, context, event): - # bpy.ops.wm.console_toggle() - # return{'FINISHED'} def execute(self, context): bpy.ops.wm.console_toggle() return {'FINISHED'} -#=========================================================================== + +# =========================================================================== # Get filepath for export -#=========================================================================== +# =========================================================================== def get_dst_path(): if bpy.context.scene.udk_option_filename_src == '0': if bpy.context.active_object: - path = os.path.split(bpy.data.filepath)[0] + "\\" + bpy.context.active_object.name# + ".psk" + path = os.path.split(bpy.data.filepath)[0] + "\\" + bpy.context.active_object.name # + ".psk" else: - #path = os.path.split(bpy.data.filepath)[0] + "\\" + "Unknown"; - path = os.path.splitext(bpy.data.filepath)[0]# + ".psk" + # path = os.path.split(bpy.data.filepath)[0] + "\\" + "Unknown"; + path = os.path.splitext(bpy.data.filepath)[0] # + ".psk" else: - path = os.path.splitext(bpy.data.filepath)[0]# + ".psk" + path = os.path.splitext(bpy.data.filepath)[0] # + ".psk" return path -#Added by [MGVS] -bpy.types.Scene.udk_option_filename_src = EnumProperty( - name = "Filename", - description = "Sets the name for the files", - 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)", - default = True) - -bpy.types.Scene.udk_option_export_psa = BoolProperty( - name = "bool export psa", - description = "Boolean for exporting psa format (Animation Data)", - default = True) - -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", - default = False) - -bpy.types.Scene.udk_option_export = EnumProperty( - name = "Export", - description = "What to export", - items = [ ('0', "Mesh only", "Exports the PSK file for the Skeletal Mesh"), - ('1', "Animation only", "Export the PSA file for Action Set(s)(Animations Data)"), - ('2', "Mesh & Animation", "Export both PSK and PSA files(Skeletal Mesh/Animation(s) Data)") ], - default = '2') - -bpy.types.Scene.udk_option_verbose = BoolProperty( - name = "Verbose", - description = "Verbose console output", - default = False) - -bpy.types.Scene.udk_option_smoothing_groups = BoolProperty( - name = "Smooth Groups", - description = "Activate hard edges as smooth groups", - default = True) - -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", - default = False) - -bpy.types.Scene.udk_option_scale = FloatProperty( - name = "UDK Scale", - description = "In case you don't want to scale objects manually - This will just scale position when on export for the skeleton mesh and animation data", - default = 1) - -#=========================================================================== + +# =========================================================================== # User interface -#=========================================================================== -class OBJECT_OT_UTSelectedFaceSmooth(bpy.types.Operator): +# =========================================================================== +class OBJECT_OT_UTSelectedFaceSmooth(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" + bl_label = "Select Smooth Faces" # "Select Smooth faces" def invoke(self, context, event): print("----------------------------------------") print("Init Select Face(s):") bselected = False for obj in bpy.data.objects: - if obj.type == 'MESH' and obj.select == True: + if obj.type == 'MESH' and obj.select is True: smoothcount = 0 flatcount = 0 - bpy.ops.object.mode_set(mode='OBJECT')#it need to go into object mode to able to select the faces - for i in bpy.context.scene.objects: i.select = False #deselect all objects - obj.select = True #set current object select - bpy.context.scene.objects.active = obj #set active object - mesh = bmesh.new(); + bpy.ops.object.mode_set(mode='OBJECT') # it need to go into object mode to able to select the faces + + for i in bpy.context.scene.objects: + i.select = False # deselect all objects + + obj.select = True # set current object select + bpy.context.scene.objects.active = obj # set active object + mesh = bmesh.new() mesh.from_mesh(obj.data) + for face in mesh.faces: face.select = False + for face in mesh.faces: - if face.smooth == True: + if face.smooth is True: face.select = True smoothcount += 1 else: @@ -1998,173 +2047,195 @@ class OBJECT_OT_UTSelectedFaceSmooth(bpy.types.Operator): mesh.to_mesh(obj.data) bpy.context.scene.update() bpy.ops.object.mode_set(mode='EDIT') - print("Select Smooth Count(s):",smoothcount," Flat Count(s):",flatcount) + print("Select Smooth Count(s):", smoothcount, " Flat Count(s):", flatcount) bselected = True break if bselected: - print("Selected Face(s) Exectue!") - self.report({'INFO'}, "Selected Face(s) Exectue!") + self.report({'INFO'}, "Selected Face(s) Executed") else: - print("Didn't select Mesh Object!") - self.report({'INFO'}, "Didn't Select Mesh Object!") + self.report({'INFO'}, "Mesh Object is not selected") print("----------------------------------------") + return{'FINISHED'} -class OBJECT_OT_MeshClearWeights(bpy.types.Operator): + +class OBJECT_OT_MeshClearWeights(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" + 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: + if obj.type == 'MESH' and obj.select is True: for vg in obj.vertex_groups: obj.vertex_groups.remove(vg) - self.report({'INFO'}, "Mesh Vertex Groups Remove!") + self.report({'INFO'}, "Mesh Vertex Groups Removed") break return{'FINISHED'} + def unpack_list(list_of_tuples): l = [] for t in list_of_tuples: l.extend(t) return l + def rebuildmesh(obj): - #make sure it in object mode - print("Mesh Object Name:",obj.name) + # make sure it in object mode + print("Mesh Object Name:", obj.name) bpy.ops.object.mode_set(mode='OBJECT') - for i in bpy.context.scene.objects: i.select = False #deselect all objects + + 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)) + me_ob = bpy.data.meshes.new(("Re_" + obj.name)) mesh = obj.data faces = [] verts = [] smoothings = [] uvfaces = [] - #print("creating array build mesh...") - mmesh = obj.to_mesh(bpy.context.scene,True,'PREVIEW') + # print("creating array build mesh...") + mmesh = obj.to_mesh(bpy.context.scene, True, 'PREVIEW') uv_layer = mmesh.tessface_uv_textures.active + for face in mmesh.tessfaces: - smoothings.append(face.use_smooth)#smooth or flat in boolean - if uv_layer is not None:#check if there texture data exist + smoothings.append(face.use_smooth) # smooth or flat in boolean + if uv_layer is not None: # check if there texture data exist faceUV = uv_layer.data[face.index] uvs = [] for uv in faceUV.uv: - uvs.append((uv[0],uv[1])) + uvs.append((uv[0], uv[1])) uvfaces.append(uvs) - #print((face.vertices[:])) + # print((face.vertices[:])) if len(face.vertices) == 3: - faces.extend([(face.vertices[0],face.vertices[1],face.vertices[2],0)]) + faces.extend([(face.vertices[0], face.vertices[1], face.vertices[2], 0)]) else: - faces.extend([(face.vertices[0],face.vertices[1],face.vertices[2],face.vertices[3])]) - #vertex positions + 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()) - #vertices weight groups into array - vertGroups = {} #array in strings + # vertices weight groups into array + vertGroups = {} # array in strings + for vgroup in obj.vertex_groups: vlist = [] for v in mesh.vertices: for vg in v.groups: if vg.group == vgroup.index: - vlist.append((v.index,vg.weight)) - #print((v.index,vg.weight)) + 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) + # 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.tessfaces.foreach_set("vertices_raw",unpack_list( faces)) - me_ob.tessfaces.foreach_set("use_smooth", smoothings)#smooth array from face + 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 + # check if there is uv faces if len(uvfaces) > 0: uvtex = me_ob.tessface_uv_textures.new(name="retex") for i, face in enumerate(me_ob.tessfaces): - blender_tface = uvtex.data[i] #face + blender_tface = uvtex.data[i] # face mfaceuv = uvfaces[i] if len(mfaceuv) == 3: - blender_tface.uv1 = mfaceuv[0]; - blender_tface.uv2 = mfaceuv[1]; - blender_tface.uv3 = mfaceuv[2]; + blender_tface.uv1 = mfaceuv[0] + blender_tface.uv2 = mfaceuv[1] + blender_tface.uv3 = mfaceuv[2] if len(mfaceuv) == 4: - blender_tface.uv1 = mfaceuv[0]; - blender_tface.uv2 = mfaceuv[1]; - blender_tface.uv3 = mfaceuv[2]; - blender_tface.uv4 = mfaceuv[3]; + blender_tface.uv1 = mfaceuv[0] + 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) + 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() - #Build tmp materials + + # Build tmp materials materialname = "ReMaterial" for matcount in mesh.materials: matdata = bpy.data.materials.new(materialname) me_ob.materials.append(matdata) - #assign face to material id + + # assign face to material id for face in mesh.tessfaces: me_ob.faces[face.index].material_index = face.material_index - #vertices weight groups + + # vertices weight groups for vgroup in vertGroups: group = obmesh.vertex_groups.new(vgroup) for v in vertGroups[vgroup]: - group.add([v[0]], v[1], 'ADD')# group.add(array[vertex id],weight,add) + group.add([v[0]], v[1], 'ADD') # group.add(array[vertex id],weight,add) bpy.context.scene.objects.link(obmesh) - #print("Mesh Material Count:",len(me_ob.materials)) + # print("Mesh Material Count:",len(me_ob.materials)) matcount = 0 - #print("MATERIAL ID OREDER:") + # print("MATERIAL ID OREDER:") for mat in me_ob.materials: - #print("-Material:",mat.name,"INDEX:",matcount) + # print("-Material:",mat.name,"INDEX:",matcount) matcount += 1 - print("Mesh Object Name:",obmesh.name) + + print("Mesh Object Name:", obmesh.name) bpy.context.scene.update() + return obmesh -class OBJECT_OT_UTRebuildMesh(bpy.types.Operator): + +class OBJECT_OT_UTRebuildMesh(Operator): """It rebuild the mesh from scrape from the selected mesh object. """ \ """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" + bl_label = "Rebuild Mesh" # "Rebuild Mesh" def invoke(self, context, event): print("----------------------------------------") print("Init Mesh Bebuild...") bselected = False bpy.ops.object.mode_set(mode='OBJECT') + for obj in bpy.data.objects: - if obj.type == 'MESH' and obj.select == True: + if obj.type == 'MESH' and obj.select is True: rebuildmesh(obj) - self.report({'INFO'}, "Rebuild Mesh Finish!") + + self.report({'INFO'}, "Rebuild Mesh Finished!") print("Finish Mesh Build...") print("----------------------------------------") return{'FINISHED'} + def rebuildarmature(obj): - currentbone = [] #select armature for roll copy - print("Armature Name:",obj.name) + currentbone = [] # select armature for roll copy + print("Armature Name:", obj.name) objectname = "ArmatureDataPSK" - meshname ="ArmatureObjectPSK" + meshname = "ArmatureObjectPSK" armdata = bpy.data.armatures.new(objectname) ob_new = bpy.data.objects.new(meshname, 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 + # bpy.ops.object.mode_set(mode='OBJECT') + + 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 is not None: - currentbone.append([bone.name,bone.roll]) + currentbone.append([bone.name, bone.roll]) else: - currentbone.append([bone.name,bone.roll]) + currentbone.append([bone.name, bone.roll]) bpy.ops.object.mode_set(mode='OBJECT') - for i in bpy.context.scene.objects: i.select = False #deselect all objects + + 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') @@ -2181,135 +2252,185 @@ def rebuildarmature(obj): parentbone = ob_new.data.edit_bones[bone.parent.name] newbone.parent = parentbone - ob_new.animation_data_create()#create animation data - if obj.animation_data is not None:#check for animation - 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) + ob_new.animation_data_create() # create animation data + if obj.animation_data is not None: # check for animation + # just make sure it here to do the animations if exist + ob_new.animation_data.action = obj.animation_data.action + + print("Armature Object Name:", ob_new.name) return ob_new -class OBJECT_OT_UTRebuildArmature(bpy.types.Operator): + +class OBJECT_OT_UTRebuildArmature(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 + bl_label = "Rebuild Armature" # Rebuild Armature def invoke(self, context, event): print("----------------------------------------") print("Init Rebuild Armature...") bselected = False for obj in bpy.data.objects: - if obj.type == 'ARMATURE' and obj.select == True: + if obj.type == 'ARMATURE' and obj.select is True: rebuildarmature(obj) self.report({'INFO'}, "Rebuild Armature Finish!") print("End of Rebuild Armature.") print("----------------------------------------") return{'FINISHED'} -class UDKActionSetListPG(bpy.types.PropertyGroup): - bool = BoolProperty(default=False) - string = StringProperty() - actionname = StringProperty() - bmatch = BoolProperty(default=False,name="Match", options={"HIDDEN"},description = "This check against bone names and action group names matches and override boolean if true") - bexport = BoolProperty(default=False,name="Export",description = "Check this to export the animation") -bpy.utils.register_class(UDKActionSetListPG) -bpy.types.Scene.udkas_list = CollectionProperty(type=UDKActionSetListPG) -bpy.types.Scene.udkas_list_idx = IntProperty() +class UDKActionSetListPG(PropertyGroup): + bool = BoolProperty(default=False) + string = StringProperty() + actionname = StringProperty() + bmatch = BoolProperty( + default=False, + name="Match", + options={"HIDDEN"}, + description="This check against bone names and action group " + "names matches and override boolean if true" + ) + bexport = BoolProperty( + default=False, + name="Export", + description="Check this to export the animation" + ) + -class UL_UDKActionSetList(bpy.types.UIList): +class UL_UDKActionSetList(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): layout.label(item.name) layout.prop(item, "bmatch", text="Match") layout.prop(item, "bexport", text="Export") -class UDKObjListPG(bpy.types.PropertyGroup): - bool = BoolProperty(default=False) - string = StringProperty() - bexport = BoolProperty(default=False,name="Export", options={"HIDDEN"},description = "This will be ignore when exported") - bselect = BoolProperty(default=False,name="Select", options={"HIDDEN"},description = "This will be ignore when exported") - otype = StringProperty(name="Type",description = "This will be ignore when exported") -bpy.utils.register_class(UDKObjListPG) -bpy.types.Scene.udkobj_list = CollectionProperty(type=UDKObjListPG) -bpy.types.Scene.udkobj_list_idx = IntProperty() +class UDKObjListPG(PropertyGroup): + bool = BoolProperty(default=False) + string = StringProperty() + bexport = BoolProperty( + default=False, + name="Export", + options={"HIDDEN"}, + description="This will be ignore when exported" + ) + bselect = BoolProperty( + default=False, + name="Select", + options={"HIDDEN"}, + description="This will be ignore when exported" + ) + otype = StringProperty( + name="Type", + description="This will be ignore when exported" + ) -class UL_UDKObjList(bpy.types.UIList): + +class UL_UDKObjList(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): layout.label(item.name) layout.prop(item, "otype", text="") layout.prop(item, "bselect", text="") -class UDKMeshListPG(bpy.types.PropertyGroup): - bool = BoolProperty(default=False) - string = StringProperty() - bexport = BoolProperty(default=False,name="Export", options={"HIDDEN"},description = "This object will be export when true") - bselect = BoolProperty(default=False,name="Select", options={"HIDDEN"},description = "Make sure you have Mesh is parent to Armature") - otype = StringProperty(name="Type",description = "This will be ignore when exported") -bpy.utils.register_class(UDKMeshListPG) -bpy.types.Scene.udkmesh_list = CollectionProperty(type=UDKMeshListPG) -bpy.types.Scene.udkmesh_list_idx = IntProperty() +class UDKMeshListPG(PropertyGroup): + bool = BoolProperty( + default=False + ) + string = StringProperty() + bexport = BoolProperty( + default=False, + name="Export", + options={"HIDDEN"}, + description="This object will be export when true" + ) + bselect = BoolProperty( + default=False, + name="Select", + options={"HIDDEN"}, + description="Make sure you have Mesh is parent to Armature" + ) + otype = StringProperty( + name="Type", + description="This will be ignore when exported" + ) + -class UL_UDKMeshList(bpy.types.UIList): +class UL_UDKMeshList(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): layout.label(item.name) - #layout.prop(item, "bselect", text="Select") + # layout.prop(item, "bselect", text="Select") layout.prop(item, "bexport", text="Export") -class UDKArmListPG(bpy.types.PropertyGroup): - bool = BoolProperty(default=False) - string = StringProperty() - bexport = BoolProperty(default=False,name="Export", options={"HIDDEN"},description = "This will be ignore when exported") - bselect = BoolProperty(default=False,name="Select", options={"HIDDEN"},description = "This will be ignore when exported") - otype = StringProperty(name="Type",description = "This will be ignore when exported") -bpy.utils.register_class(UDKArmListPG) -bpy.types.Scene.udkArm_list = CollectionProperty(type=UDKArmListPG) -bpy.types.Scene.udkArm_list_idx = IntProperty() +class UDKArmListPG(PropertyGroup): + bool = BoolProperty(default=False) + string = StringProperty() + bexport = BoolProperty( + default=False, + name="Export", + options={"HIDDEN"}, + description="This will be ignore when exported" + ) + bselect = BoolProperty( + default=False, + name="Select", + options={"HIDDEN"}, + description="This will be ignore when exported" + ) + otype = StringProperty( + name="Type", + description="This will be ignore when exported" + ) + -class UL_UDKArmList(bpy.types.UIList): +class UL_UDKArmList(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): layout.label(item.name) -class Panel_UDKExport( bpy.types.Panel ): - bl_label = "UDK Export" - bl_idname = "OBJECT_PT_udk_tools" - bl_category = "File I/O" - bl_space_type = "VIEW_3D" - bl_region_type = "TOOLS" +class Panel_UDKExport(Panel): + bl_label = "UDK Export" + bl_idname = "OBJECT_PT_udk_tools" + bl_category = "File I/O" + bl_space_type = "VIEW_3D" + bl_region_type = "TOOLS" bl_context = "objectmode" - #def draw_header(self, context): - # layout = self.layout - #obj = context.object - #layout.prop(obj, "select", text="") + """ + 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 + @classmethod + def poll(cls, context): + return context.active_object + """ def draw(self, context): layout = self.layout path = get_dst_path() object_name = "" - #if context.object: - # object_name = context.object.name + """ + if context.object: + object_name = context.object.name + """ if context.active_object: object_name = context.active_object.name row10 = layout.row() row10.prop(context.scene, "udk_option_smoothing_groups") row10.prop(context.scene, "udk_option_clamp_uv") row10.prop(context.scene, "udk_option_verbose") + row = layout.row() row.label(text="Active object: " + object_name) - #layout.separator() layout.prop(context.scene, "udk_option_filename_src") row = layout.row() row.label(text=path) - #layout.separator() + layout.prop(context.scene, "udk_option_export") layout.prop(context.scene, "udk_option_selectobjects") @@ -2322,15 +2443,17 @@ class Panel_UDKExport( bpy.types.Panel ): layout.template_list("UL_UDKMeshList", "", context.scene, "udkmesh_list", context.scene, "udkmesh_list_idx", rows=5) layout.prop(context.scene, "udk_option_selectanimations") + if context.scene.udk_option_selectanimations: layout.operator("action.setanimupdate") layout.label(text="Action Set(s) - Match / Export") layout.template_list("UL_UDKActionSetList", "", context.scene, "udkas_list", context.scene, "udkas_list_idx", rows=5) - test = layout.separator() + layout.separator() layout.prop(context.scene, "udk_option_scale") layout.prop(context.scene, "udk_option_rebuildobjects") - #layout.prop(context.scene, "udk_option_ignoreactiongroupnames") + # layout.prop(context.scene, "udk_option_ignoreactiongroupnames") + row11 = layout.row() row11.operator("object.udk_export") row11.operator("object.toggle_console") @@ -2341,79 +2464,85 @@ class Panel_UDKExport( bpy.types.Panel ): layout.operator(OBJECT_OT_UTRebuildMesh.bl_idname) layout.operator(OBJECT_OT_UDKCheckMeshLines.bl_idname) + def udkupdateobjects(): - my_objlist = bpy.context.scene.udkArm_list - objectl = [] - for objarm in bpy.context.scene.objects:#list and filter only mesh and armature - if objarm.type == 'ARMATURE': - objectl.append(objarm) - for _objd in objectl:#check if list has in udk list - bfound_obj = False - for _obj in my_objlist: - if _obj.name == _objd.name and _obj.otype == _objd.type: - _obj.bselect = _objd.select - bfound_obj = True - break - if bfound_obj == False: - #print("ADD ARMATURE...") - my_item = my_objlist.add() - my_item.name = _objd.name - my_item.bselect = _objd.select - my_item.otype = _objd.type - removeobject = [] - for _udkobj in my_objlist: - bfound_objv = False - for _objd in bpy.context.scene.objects: #check if there no existing object from sense to remove it - if _udkobj.name == _objd.name and _udkobj.otype == _objd.type: - bfound_objv = True - break - if bfound_objv == False: - removeobject.append(_udkobj) - #print("remove check...") - for _item in removeobject: #loop remove object from udk list object - count = 0 - for _obj in my_objlist: - if _obj.name == _item.name and _obj.otype == _item.otype: - 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 - if objarm.type == 'MESH': - objectl.append(objarm) - for _objd in objectl:#check if list has in udk list - bfound_obj = False - for _obj in my_objlist: - if _obj.name == _objd.name and _obj.otype == _objd.type: - _obj.bselect = _objd.select - bfound_obj = True - break - if bfound_obj == False: - my_item = my_objlist.add() - my_item.name = _objd.name - my_item.bselect = _objd.select - my_item.otype = _objd.type - removeobject = [] - for _udkobj in my_objlist: - bfound_objv = False - for _objd in bpy.context.scene.objects: #check if there no existing object from sense to remove it - if _udkobj.name == _objd.name and _udkobj.otype == _objd.type: - bfound_objv = True - break - if bfound_objv == False: - removeobject.append(_udkobj) - #print("remove check...") - for _item in removeobject: #loop remove object from udk list object - count = 0 - for _obj in my_objlist: - if _obj.name == _item.name and _obj.otype == _item.otype: - my_objlist.remove(count) - break - count += 1 + my_objlist = bpy.context.scene.udkArm_list + objectl = [] + for objarm in bpy.context.scene.objects: # list and filter only mesh and armature + if objarm.type == 'ARMATURE': + objectl.append(objarm) + + for _objd in objectl: # check if list has in udk list + bfound_obj = False + for _obj in my_objlist: + if _obj.name == _objd.name and _obj.otype == _objd.type: + _obj.bselect = _objd.select + bfound_obj = True + break + + if bfound_obj is False: + # print("ADD ARMATURE...") + my_item = my_objlist.add() + my_item.name = _objd.name + my_item.bselect = _objd.select + my_item.otype = _objd.type + removeobject = [] + for _udkobj in my_objlist: + bfound_objv = False + + for _objd in bpy.context.scene.objects: # check if there no existing object from sense to remove it + if _udkobj.name == _objd.name and _udkobj.otype == _objd.type: + bfound_objv = True + break -class OBJECT_OT_UDKObjUpdate(bpy.types.Operator): + if bfound_objv is False: + removeobject.append(_udkobj) + # print("remove check...") + for _item in removeobject: # loop remove object from udk list object + count = 0 + for _obj in my_objlist: + if _obj.name == _item.name and _obj.otype == _item.otype: + 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 + if objarm.type == 'MESH': + objectl.append(objarm) + for _objd in objectl: # check if list has in udk list + bfound_obj = False + for _obj in my_objlist: + if _obj.name == _objd.name and _obj.otype == _objd.type: + _obj.bselect = _objd.select + bfound_obj = True + break + if bfound_obj is False: + my_item = my_objlist.add() + my_item.name = _objd.name + my_item.bselect = _objd.select + my_item.otype = _objd.type + removeobject = [] + for _udkobj in my_objlist: + bfound_objv = False + for _objd in bpy.context.scene.objects: # check if there no existing object from sense to remove it + if _udkobj.name == _objd.name and _udkobj.otype == _objd.type: + bfound_objv = True + break + if bfound_objv is False: + removeobject.append(_udkobj) + # print("remove check...") + for _item in removeobject: # loop remove object from udk list object + count = 0 + for _obj in my_objlist: + if _obj.name == _item.name and _obj.otype == _item.otype: + my_objlist.remove(count) + break + count += 1 + + +class OBJECT_OT_UDKObjUpdate(Operator): """This will update the filter of the mesh and armature""" bl_idname = "object.selobjectpdate" bl_label = "Update Object(s)" @@ -2424,21 +2553,26 @@ class OBJECT_OT_UDKObjUpdate(bpy.types.Operator): udkupdateobjects() return{'FINISHED'} + def udkcheckmeshline(): objmesh = None for obj in bpy.context.scene.objects: - if obj.type == 'MESH' and obj.select == True: + if obj.type == 'MESH' and obj.select is True: objmesh = obj - objmesh = triangulate_mesh(objmesh) #create a copy of the mesh + + objmesh = triangulate_mesh(objmesh) # create a copy of the mesh bpy.ops.object.mode_set(mode='OBJECT') - for i in bpy.context.scene.objects: i.select = False # deselect all objects + + for i in bpy.context.scene.objects: + i.select = False # deselect all objects + objmesh.select = True - bpy.context.scene.objects.active = objmesh #set active mesh - wedges = ObjMap() - points = ObjMap() - bpy.ops.object.mode_set(mode='EDIT') #set in edit mode + bpy.context.scene.objects.active = objmesh # set active mesh + wedges = ObjMap() + points = ObjMap() + 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 + bpy.context.tool_settings.mesh_select_mode = (True, False, False) # select vertices if objmesh is not None: print("found mesh") @@ -2446,19 +2580,19 @@ def udkcheckmeshline(): print(objmesh.data.tessfaces) vertex_list = [] for face in objmesh.data.tessfaces: - wedge_list = [] - vect_list = [] + wedge_list = [] + vect_list = [] for i in range(3): - vert_index = face.vertices[i] - vert = objmesh.data.vertices[vert_index] - vect_list.append( FVector(vert.co.x, vert.co.y, vert.co.z) ) + vert_index = face.vertices[i] + vert = objmesh.data.vertices[vert_index] + vect_list.append(FVector(vert.co.x, vert.co.y, vert.co.z)) vpos = objmesh.matrix_local * vert.co - p = VPoint() - p.Point.X = vpos.x - p.Point.Y = vpos.y - p.Point.Z = vpos.z - w = VVertex() - w.PointIndex = points.get(p) # store keys + p = VPoint() + p.Point.X = vpos.x + p.Point.Y = vpos.y + p.Point.Z = vpos.z + w = VVertex() + w.PointIndex = points.get(p) # store keys index_wedge = wedges.get(w) wedge_list.append(index_wedge) no = face.normal @@ -2472,28 +2606,29 @@ def udkcheckmeshline(): elif dot < 0: (tri.WedgeIndex0, tri.WedgeIndex1, tri.WedgeIndex2) = wedge_list else: - dindex0 = face.vertices[0]; - dindex1 = face.vertices[1]; - dindex2 = face.vertices[2]; + dindex0 = face.vertices[0] + dindex1 = face.vertices[1] + dindex2 = face.vertices[2] 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 - if vertex.index == vl: #if match set to select + for vertex in objmesh.data.vertices: # loop all vertex in the mesh list + for vl in vertex_list: # loop for error vertex + if vertex.index == vl: # if match set to select vertex.select = True break - bpy.ops.object.mode_set(mode='EDIT') #set in edit mode to see the select vertex - objmesh.data.update() # update object - bpy.context.scene.update() #update scene + bpy.ops.object.mode_set(mode='EDIT') # set in edit mode to see the select vertex + objmesh.data.update() # update object + bpy.context.scene.update() # update scene message = "MESH PASS" if len(vertex_list) > 0: message = "MESH FAIL" return message -class OBJECT_OT_UDKCheckMeshLines(bpy.types.Operator): + +class OBJECT_OT_UDKCheckMeshLines(Operator): """Select the mesh for export test. This will create dummy mesh to see which area are broken. """ \ """If the vertices share the same position it will cause a bug""" bl_idname = "object.udkcheckmeshline" @@ -2504,7 +2639,8 @@ class OBJECT_OT_UDKCheckMeshLines(bpy.types.Operator): self.report({'ERROR'}, message) return{'FINISHED'} -class OBJECT_OT_ActionSetAnimUpdate(bpy.types.Operator): + +class OBJECT_OT_ActionSetAnimUpdate(Operator): """Select Armture to match the action set groups. """ \ """All bones keys must be set to match with number of bones""" bl_idname = "action.setanimupdate" @@ -2521,9 +2657,9 @@ class OBJECT_OT_ActionSetAnimUpdate(bpy.types.Operator): armatureselected = [] for objarm in bpy.context.scene.objects: if objarm.type == 'ARMATURE': - #print("ADDED ARMATURE...") + # print("ADDED ARMATURE...") armatures.append(objarm) - if objarm.select == True: + if objarm.select is True: armatureselected.append(objarm) if len(armatureselected) == len(armatures) == 1: @@ -2537,15 +2673,15 @@ class OBJECT_OT_ActionSetAnimUpdate(bpy.types.Operator): for bone in armature.pose.bones: bones.append(bone.name) - for action in bpy.data.actions:#action list + for action in bpy.data.actions: # action list bfound = False count = 0 for actionbone in action.groups: - #print("Pose bone name: ",actionbone.name) + # print("Pose bone name: ",actionbone.name) for b in bones: if b == actionbone.name: count += 1 - #print(b," : ",actionbone.name) + # print(b," : ",actionbone.name) break for actionlist in my_sett: if action.name == actionlist.name: @@ -2556,17 +2692,17 @@ class OBJECT_OT_ActionSetAnimUpdate(bpy.types.Operator): actionlist.bmatch = False bfound = True break - if bfound != True: + if bfound is not True: my_item = my_sett.add() - #print(dir(my_item.bmatch)) + # print(dir(my_item.bmatch)) my_item.name = action.name - #my_item.template_list_controls = "bmatch:bexport" + # my_item.template_list_controls = "bmatch:bexport" if len(bones) == len(action.groups) == count: my_item.bmatch = True else: my_item.bmatch = False removeactions = [] - #check action list and data actions + # check action list and data actions for actionlist in bpy.context.scene.udkas_list: bfind = False notfound = 0 @@ -2575,26 +2711,27 @@ class OBJECT_OT_ActionSetAnimUpdate(bpy.types.Operator): bfind = True else: notfound += 1 - #print("ACT NAME:",actionlist.name," COUNT",notfound) + # print("ACT NAME:",actionlist.name," COUNT",notfound) if notfound == len(bpy.data.actions): - #print("remove :",actionlist.name) + # print("remove :",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 + # print("Not in the action data list:",len(removeactions)) + # remove list or chnages in the name the template list for actname in removeactions: actioncount = 0 for actionlist in my_sett: - #print("action name:",actionlist.name) + # print("action name:",actionlist.name) if actionlist.name == actname: - my_sett.remove(actioncount); + my_sett.remove(actioncount) break actioncount += 1 return{'FINISHED'} -class ExportUDKAnimData(bpy.types.Operator): + +class ExportUDKAnimData(Operator): """Export Skeleton Mesh / Animation Data file(s). """ \ """One mesh and one armature else select one mesh or armature to be exported""" - bl_idname = "export_anim.udk" # this is important since its how bpy.ops.export.udk_anim_data is constructed + bl_idname = "export_anim.udk" # this is important since its how bpy.ops.export.udk_anim_data is constructed bl_label = "Export PSK/PSA" # List of operator properties, the attributes will be assigned @@ -2607,13 +2744,18 @@ class ExportUDKAnimData(bpy.types.Operator): default="*.psk;*.psa", options={'HIDDEN'}, ) - udk_option_smoothing_groups = bpy.types.Scene.udk_option_smoothing_groups - udk_option_clamp_uv = bpy.types.Scene.udk_option_clamp_uv - udk_option_verbose = bpy.types.Scene.udk_option_verbose - udk_option_filename_src = bpy.types.Scene.udk_option_filename_src - 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 + udk_option_scale = FloatProperty( + name="UDK Scale", + description="In case you don't want to scale objects manually - " + "This will just scale position when on export for the skeleton mesh and animation data", + default=1 + ) + 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 + ) @classmethod def poll(cls, context): @@ -2645,61 +2787,217 @@ class ExportUDKAnimData(bpy.types.Operator): self.report({'WARNING', 'INFO'}, message) return {'FINISHED'} + def draw(self, context): + layout = self.layout + scene = context.scene + + layout.prop(scene, "udk_option_smoothing_groups") + layout.prop(scene, "udk_option_clamp_uv") + layout.prop(scene, "udk_option_verbose") + layout.prop(scene, "udk_option_filename_src") + layout.prop(scene, "udk_option_export") + layout.prop(self, "udk_option_scale") + layout.prop(self, "udk_option_rebuildobjects") + def invoke(self, context, event): + self.udk_option_scale = bpy.context.scene.udk_option_scale + self.udk_option_rebuildobjects = bpy.context.scene.udk_option_rebuildobjects + 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 + self.layout.operator(ExportUDKAnimData.bl_idname, + text="Skeleton Mesh / Animation Data (.psk/.psa)").filepath = default_path + + +# Add-ons Preferences Update Panel + +# Define Panel classes for updating +panels = ( + Panel_UDKExport, + ) + -## Addons Preferences Update Panel def update_panel(self, context): + message = "Export Unreal Engine Format(.psk/.psa): Updating Panel locations has failed" try: - bpy.utils.unregister_class(Panel_UDKExport) - except: + for panel in panels: + if "bl_rna" in panel.__dict__: + bpy.utils.unregister_class(panel) + + for panel in panels: + panel.bl_category = context.user_preferences.addons[__name__].preferences.category + bpy.utils.register_class(panel) + + except Exception as e: + print("\n[{}]\n{}\n\nError:\n{}".format(__name__, message, e)) pass - Panel_UDKExport.bl_category = context.user_preferences.addons[__name__].preferences.category - bpy.utils.register_class(Panel_UDKExport) -class PskAddonPreferences(bpy.types.AddonPreferences): + +class PskAddonPreferences(AddonPreferences): # this must match the addon name, use '__package__' # when defining this in a submodule of a python package. bl_idname = __name__ - category = bpy.props.StringProperty( + category = StringProperty( name="Tab Category", description="Choose a name for the category of the panel", default="File I/O", - update=update_panel) + update=update_panel + ) def draw(self, context): - layout = self.layout + row = layout.row() col = row.column() col.label(text="Tab Category:") col.prop(self, "category", text="") -#=========================================================================== + + +# =========================================================================== # Entry -#=========================================================================== +# =========================================================================== def register(): - #print("REGISTER") + bpy.utils.register_module(__name__) bpy.types.INFO_MT_file_export.append(menu_func) update_panel(None, bpy.context) + # Added by [MGVS] + bpy.types.Scene.udk_option_filename_src = EnumProperty( + name="Filename", + description="Sets the name for the files", + 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)", + default=True + ) + bpy.types.Scene.udk_option_export_psa = BoolProperty( + name="bool export psa", + description="Boolean for exporting psa format (Animation Data)", + default=True + ) + 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", + default=False + ) + bpy.types.Scene.udk_option_export = EnumProperty( + name="Export", + description="What to export", + items=[ + ('0', "Mesh only", "Exports the PSK file for the Skeletal Mesh"), + ('1', "Animation only", "Export the PSA file for Action Set(s)(Animations Data)"), + ('2', "Mesh & Animation", "Export both PSK and PSA files(Skeletal Mesh/Animation(s) Data)") + ], + default='2' + ) + bpy.types.Scene.udk_option_verbose = BoolProperty( + name="Verbose", + description="Verbose console output", + default=False + ) + bpy.types.Scene.udk_option_smoothing_groups = BoolProperty( + name="Smooth Groups", + description="Activate hard edges as smooth groups", + default=True + ) + 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", + default=False + ) + bpy.types.Scene.udk_option_scale = FloatProperty( + name="UDK Scale", + description="In case you don't want to scale objects manually - " + "This will just scale position when on export for the skeleton mesh and animation data", + default=1 + ) + bpy.types.Scene.udkas_list = CollectionProperty( + type=UDKActionSetListPG + ) + bpy.types.Scene.udkas_list_idx = IntProperty() + bpy.types.Scene.udkobj_list = CollectionProperty( + type=UDKObjListPG + ) + bpy.types.Scene.udkobj_list_idx = IntProperty() + bpy.types.Scene.udkmesh_list = CollectionProperty( + type=UDKMeshListPG + ) + bpy.types.Scene.udkmesh_list_idx = IntProperty() + bpy.types.Scene.udkArm_list = CollectionProperty( + type=UDKArmListPG + ) + bpy.types.Scene.udkArm_list_idx = IntProperty() + + def unregister(): - #print("UNREGISTER") bpy.utils.unregister_module(__name__) bpy.types.INFO_MT_file_export.remove(menu_func) + del bpy.types.Scene.udk_option_filename_src + del bpy.types.Scene.udk_option_export_psk + del bpy.types.Scene.udk_option_export_psa + del bpy.types.Scene.udk_option_clamp_uv + del bpy.types.Scene.udk_copy_merge + del bpy.types.Scene.udk_option_export + del bpy.types.Scene.udk_option_verbose + del bpy.types.Scene.udk_option_smoothing_groups + del bpy.types.Scene.udk_option_triangulate + del bpy.types.Scene.udk_option_selectanimations + del bpy.types.Scene.udk_option_selectobjects + del bpy.types.Scene.udk_option_rebuildobjects + del bpy.types.Scene.udk_option_ignoreactiongroupnames + del bpy.types.Scene.udk_option_scale + del bpy.types.Scene.udkas_list + del bpy.types.Scene.udkas_list_idx + del bpy.types.Scene.udkobj_list + del bpy.types.Scene.udkobj_list_idx + del bpy.types.Scene.udkmesh_list + del bpy.types.Scene.udkmesh_list_idx + del bpy.types.Scene.udkArm_list + del bpy.types.Scene.udkArm_list_idx + + 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')) |