From 58fe3b009eb994c17e3324d1cf536db63f39cae5 Mon Sep 17 00:00:00 2001
From: John Phan <darkneter@gmail.com>
Date: Sun, 17 Apr 2011 19:59:41 +0000
Subject: [PATCH] clean and fixed up vertex groups.

---
 io_export_unreal_psk_psa.py | 342 ++++++++++++++++++++----------------
 1 file changed, 191 insertions(+), 151 deletions(-)

diff --git a/io_export_unreal_psk_psa.py b/io_export_unreal_psk_psa.py
index 5b91795f3..48aeb29b9 100644
--- a/io_export_unreal_psk_psa.py
+++ b/io_export_unreal_psk_psa.py
@@ -83,11 +83,12 @@ import time
 import datetime
 import bpy
 import mathutils
+import random
 import operator
 import sys
 
-from struct import pack, calcsize
 
+from struct import pack, calcsize
 
 # REFERENCE MATERIAL JUST IN CASE:
 # 
@@ -112,6 +113,12 @@ SIZE_VPOINT = 12
 SIZE_VTRIANGLE = 12
 MaterialName = []
 
+# ======================================================================
+# TODO: remove this 1am hack
+nbone = 0
+bDeleteMergeMesh = False
+exportmessage = "Export Finish" 
+
 ########################################################################
 # Generic Object->Integer mapping
 # the object must be usable as a dictionary key
@@ -609,7 +616,6 @@ def is_1d_face(blender_face,mesh):
 
 ##################################################
 # http://en.wikibooks.org/wiki/Blender_3D:_Blending_Into_Python/Cookbook#Triangulate_NMesh
-bDeleteMergeMesh = False
 #blender 2.50 format using the Operators/command convert the mesh to tri mesh
 def triangulateNMesh(object):
     global bDeleteMergeMesh
@@ -654,46 +660,10 @@ def triangulateNMesh(object):
         me_ob = object
     return me_ob
 
-#Blender Bone Index
-class BBone:
-    def __init__(self):
-        self.bone = ""
-        self.index = 0
-bonedata = []
-BBCount = 0    
-#deal with mesh bones groups vertex point
-def BoneIndex(bone):
-    global BBCount, bonedata
-    #print("//==============")
-    #print(bone.name , "ID:",BBCount)
-    BB = BBone()
-    BB.bone = bone.name
-    BB.index = BBCount
-    bonedata.append(BB)
-    BBCount += 1
-    for current_child_bone in bone.children:
-        BoneIndex(current_child_bone)
-
-def BoneIndexArmature(blender_armature):
-    global BBCount
-    #print("\n Buildng bone before mesh \n")
-    #objectbone = blender_armature.pose #Armature bone
-    #print(blender_armature)
-    objectbone = blender_armature[0].pose 
-    #print(dir(ArmatureData))
-    
-    for bone in objectbone.bones:
-        if(bone.parent is None):
-            BoneIndex(bone)
-            #BBCount += 1
-            break
-    
-
 # Actual object parsing functions
 def parse_meshes(blender_meshes, psk_file):
     #this is use to call the bone name and the index array for group index matches
-    global bonedata,bDeleteMergeMesh
-    #print("BONE DATA",len(bonedata))
+    global bDeleteMergeMesh
     print ("----- parsing meshes -----")
     print("Number of Object Meshes:",len(blender_meshes))
     for current_obj in blender_meshes: #number of mesh that should be one mesh here
@@ -888,11 +858,8 @@ def parse_meshes(blender_meshes, psk_file):
                     tri.SmoothingGroups = 0
                 
                 tri.MatIndex = object_material_index
-                
-                
                 #print(tri)
-                psk_file.AddFace(tri)
-                
+                psk_file.AddFace(tri)                
             else:
                 discarded_face_count = discarded_face_count + 1
                 
@@ -902,7 +869,7 @@ def parse_meshes(blender_meshes, psk_file):
         if len(points.dict) > 32767:
            raise RuntimeError("Vertex point reach max limited 32767 in pack data. Your",len(points.dict))
         print (" -- Dumping Mesh Wedge -- LEN:",len(wedges.dict))
-        		
+        
         for wedge in wedges.items():
             psk_file.AddWedge(wedge)
             
@@ -919,16 +886,17 @@ def parse_meshes(blender_meshes, psk_file):
         #verts to bones for influences by having the VertexGroup named the same thing as
         #the bone
 
-        #vertex group.
-        for bonegroup in bonedata:
-            #print("bone gourp build:",bonegroup.bone)
+        #vertex group
+        for obvgroup in current_obj.vertex_groups:
+            #print("bone gourp build:",obvgroup.name)#print bone name
+            #print(dir(obvgroup))
             vert_list = []
             for current_vert in current_mesh.vertices:
                 #print("INDEX V:",current_vert.index)
                 vert_index = current_vert.index
                 for vgroup in current_vert.groups:#vertex groupd id
                     vert_weight = vgroup.weight
-                    if(bonegroup.index == vgroup.group):
+                    if(obvgroup.index == vgroup.group):
                         p = VPoint()
                         vpos = current_vert.co * current_obj.matrix_local
                         p.Point.X = vpos.x
@@ -939,8 +907,8 @@ def parse_meshes(blender_meshes, psk_file):
                         v_item = (point_index, vert_weight)
                         vert_list.append(v_item)
             #bone name, [point id and wieght]
-            #print("Add Vertex Group:",bonegroup.bone, " No. Points:",len(vert_list))
-            psk_file.VertexGroups[bonegroup.bone] = vert_list
+            #print("Add Vertex Group:",obvgroup.name, " No. Points:",len(vert_list))
+            psk_file.VertexGroups[obvgroup.name] = vert_list
         
         #unrealtriangulatebool #this will remove the mesh from the scene
         '''
@@ -967,7 +935,6 @@ def parse_meshes(blender_meshes, psk_file):
         
 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
@@ -986,9 +953,6 @@ def make_fquat_default(bquat):
     quat.W = bquat.w
     return quat
 
-# =================================================================================================
-# TODO: remove this 1am hack
-nbone = 0
 def parse_bone(blender_bone, psk_file, psa_file, parent_id, is_root_bone, parent_matrix, parent_root):
     global nbone     # look it's evil!
     #print '-------------------- Dumping Bone ---------------------- '
@@ -997,7 +961,6 @@ def parse_bone(blender_bone, psk_file, psa_file, parent_id, is_root_bone, parent
     if blender_bone.parent is None:
         parent_root = blender_bone
     
-    
     child_count = len(blender_bone.children)
     #child of parent
     child_parent = blender_bone.parent
@@ -1164,9 +1127,8 @@ def parse_animation(blender_scene, blender_armatures, psa_file):
     print ("Default FPS: 24" )
     
     cur_frame_index = 0
-    
-    if bpy.context.scene.unrealactionexportall :#if exporting all actions is ture then go do some work.
-        print("Exporting all action:",bpy.context.scene.unrealactionexportall)
+    if (bpy.context.scene.UEActionSetSettings == '1') or (bpy.context.scene.UEActionSetSettings == '2'):
+        print("Action Set(s) Settings Idx:",bpy.context.scene.UEActionSetSettings)
         print("[==== Action list Start====]")
         
         print("Number of Action set(s):",len(bpy.data.actions))
@@ -1186,7 +1148,8 @@ def parse_animation(blender_scene, blender_armatures, psa_file):
         
         for arm in blender_armatures:
             amatureobject = arm
-            
+        #print(dir(amatureobject))
+        collection = amatureobject.myCollectionUEA #collection of the object
         print("\n[==== Armature Object ====]")
         if amatureobject != None:
             print("Name:",amatureobject.name)
@@ -1196,6 +1159,20 @@ def parse_animation(blender_scene, blender_armatures, psa_file):
         print("[=========================]")
         
         for ActionNLA in bpy.data.actions:
+            FoundAction = True
+            if bpy.context.scene.UEActionSetSettings == '2':                
+                for c in collection:
+                    if c.name == ActionNLA.name:
+                        if c.mybool == True:
+                            FoundAction = True
+                        else:
+                            FoundAction = False
+                        break
+                if FoundAction == False:
+                    print("========================================")
+                    print("Skipping Action Set!",ActionNLA.name)
+                    print("========================================")
+                    #break
             print("\n==== Action Set ====")
             nobone = 0
             baction = True
@@ -1209,14 +1186,14 @@ def parse_animation(blender_scene, blender_armatures, psa_file):
                         break
             #if action groups matches the bones length and names matching the gourps do something
             if (len(ActionNLA.groups) == len(bonenames)) and (nobone == len(ActionNLA.groups)):
-                print("Action Set match: Pass")
+                #print("Action Set match: Pass")
                 baction = True
             else:
-                print("Action Set match: Fail")
-                print("Action Name:",ActionNLA.name)
+                #print("Action Set match: Fail")
+                #print("Action Name:",ActionNLA.name)
                 baction = False
             
-            if baction == True:
+            if (baction == True) and (FoundAction == True):
                 arm = amatureobject #set armature object
                 if not arm.animation_data:
                     print("======================================")
@@ -1231,7 +1208,10 @@ def parse_animation(blender_scene, blender_armatures, psa_file):
                     print("Armature has no animation, skipping...")
                     print("======================================")
                     break
+                print("Last Action Name:",arm.animation_data.action.name)
                 arm.animation_data.action = ActionNLA
+                print("Set Action Name:",arm.animation_data.action.name)
+                bpy.context.scene.update()
                 act = arm.animation_data.action
                 action_name = act.name
                 
@@ -1243,7 +1223,7 @@ def parse_animation(blender_scene, blender_armatures, psa_file):
                     
                 #this deal with action export control
                 if bHaveAction == True:
-                    
+                    print("------------------------------------")
                     print("Action Name:",action_name)
                     #look for min and max frame that current set keys
                     framemin, framemax = act.frame_range
@@ -1314,13 +1294,41 @@ def parse_animation(blender_scene, blender_armatures, psa_file):
                             head = pose_bone.head
                             
                             posebonemat = mathutils.Matrix(pose_bone.matrix)
+                            #print("quat",posebonemat)
+                            #
+                            # Error looop action get None in matrix
+                            # looping on each armature give invert and normalize for None
+                            #
                             parent_pose = pose_bone.parent
                             if parent_pose != None:
                                 parentposemat = mathutils.Matrix(parent_pose.matrix)
                                 #blender 2.4X it been flip around with new 2.50 (mat1 * mat2) should now be (mat2 * mat1)
+                                #print("posebonemat",posebonemat)
+                                #print("parentposemat",parentposemat.inverted())
+                                #print("parentposemat",dir(parentposemat))
+                                '''
+                                parentposematinvert = parentposemat.invert()
+                                if parentposematinvert == None:
+                                    posebonemat = parentposemat.inverted() * posebonemat
+                                else:
+                                    posebonemat = parentposemat.invert() * posebonemat
+                                '''
                                 posebonemat = parentposemat.invert() * posebonemat
+                                    
                             head = posebonemat.to_translation()
                             quat = posebonemat.to_quaternion().normalize()
+                            #print("to_quaternion:",posebonemat.to_quaternion())
+                            quat2 = mathutils.Quaternion(posebonemat.to_quaternion())
+                            #print("to_quaternion2:",dir(quat2))
+                            #if quat == None:
+                                #quat = posebonemat.to_quaternion().normalized()
+                            
+                            
+                            #print("quat",(posebonemat.to_quaternion().normalized()))
+                            #print("quat",dir(posebonemat.to_quaternion().normalized()))
+                            #print("pose_bone name:",pose_bone.name)
+                            #print("head",head)
+                            #print("quat",quat)
                             vkey = VQuatAnimKey()
                             vkey.Position.X = head.x
                             vkey.Position.Y = head.y
@@ -1343,21 +1351,20 @@ def parse_animation(blender_scene, blender_armatures, psa_file):
                             
                             #print ("Diff = ", diff)
                             vkey.Time = float(diff)/float(anim_rate)
-                            
                             psa_file.AddRawKey(vkey)
                             
                     #done looping frames
                     #done looping armatures
                     #continue adding animInfoBinary counts here
                 
-                    anim.TotalBones = len(unique_bone_indexes)
-                    print("Bones Count:",anim.TotalBones)
-                    anim.TrackTime = float(frame_count) / anim.AnimRate
-                    print("Time Track Frame:",anim.TrackTime)
-                    psa_file.AddAnimation(anim)
+                anim.TotalBones = len(unique_bone_indexes)
+                print("Bones Count:",anim.TotalBones)
+                anim.TrackTime = float(frame_count) / anim.AnimRate
+                print("Time Track Frame:",anim.TrackTime)
+                psa_file.AddAnimation(anim)
         print("==== Finish Action Build(s) ====")
     else:
-        print("Exporting one action:",bpy.context.scene.unrealactionexportall)
+        print("[==== Action Set Single Export====]")
         #list of armature objects
         for arm in blender_armatures:
             #check if there animation data from armature or something
@@ -1387,8 +1394,7 @@ def parse_animation(blender_scene, blender_armatures, psa_file):
                 
             #this deal with action export control
             if bHaveAction == True:
-                print("")
-                print("==== Action Set ====")
+                print("---- Action Start ----")
                 print("Action Name:",action_name)
                 #look for min and max frame that current set keys
                 framemin, framemax = act.frame_range
@@ -1492,7 +1498,6 @@ def parse_animation(blender_scene, blender_armatures, psa_file):
                         
                         #print ("Diff = ", diff)
                         vkey.Time = float(diff)/float(anim_rate)
-                        
                         psa_file.AddRawKey(vkey)
                         
                 #done looping frames
@@ -1504,10 +1509,9 @@ def parse_animation(blender_scene, blender_armatures, psa_file):
                 anim.TrackTime = float(frame_count) / anim.AnimRate
                 print("Time Track Frame:",anim.TrackTime)
                 psa_file.AddAnimation(anim)
-                print("==== Finish Action Build(s) ====")
+                print("---- Action End ----")
+                print("==== Finish Action Build ====")
     
-exportmessage = "Export Finish" 
-
 def meshmerge(selectedobjects):
     bpy.ops.object.mode_set(mode='OBJECT')
     cloneobjects = []
@@ -1538,9 +1542,7 @@ def meshmerge(selectedobjects):
         
 def fs_callback(filename, context):
     #this deal with repeat export and the reset settings
-    global bonedata, BBCount, nbone, exportmessage,bDeleteMergeMesh
-    bonedata = []#clear array
-    BBCount = 0
+    global nbone, exportmessage, bDeleteMergeMesh
     nbone = 0
     
     start_time = time.clock()
@@ -1684,8 +1686,6 @@ def fs_callback(filename, context):
         exportmessage = "Export Finish!"
         #print("blender_armature:",dir(blender_armature[0]))
         #print(blender_armature[0].scale)
-        #need to build a temp bone index for mesh group vertex
-        BoneIndexArmature(blender_armature)
 
         try:
             #######################
@@ -1792,17 +1792,17 @@ bpy.types.Scene.unrealexport_settings = EnumProperty(
     name="Export:",
     description="Select a export settings (psk/psa/all)...",
     items = exporttypedata, default = '0')
-        
+	
+bpy.types.Scene.UEActionSetSettings = EnumProperty(
+    name="Action Set(s) Export Type",
+    description="For Exporting Single, All, and Select Action Set(s).",
+    items = [("0","Single","Single Action Set Export"),("1","All","All Action Sets Export"),("2","Select","Select Action Set(s) Export")], default = '0')        
+	
 bpy.types.Scene.unrealtriangulatebool = BoolProperty(
     name="Triangulate Mesh",
     description="Convert Quad to Tri Mesh Boolean...",
     default=False)
     
-bpy.types.Scene.unrealactionexportall = BoolProperty(
-    name="All Actions",
-    description="This let you export all the actions from current armature that matches bone name in action groups names.",
-    default=False)
-
 bpy.types.Scene.unrealdisplayactionsets = BoolProperty(
     name="Show Action Set(s)",
     description="Display Action Sets Information.",
@@ -1817,6 +1817,75 @@ bpy.types.Scene.unrealexportpsa = BoolProperty(
     name="bool export psa",
     description="bool for exporting this psa format",
     default=True)
+	
+class UEAPropertyGroup(bpy.types.PropertyGroup):
+    ## create Properties for the collection entries:
+    mystring = bpy.props.StringProperty()
+    mybool = bpy.props.BoolProperty(name="Export",description="Check if you want to export the action set.",default = False)
+	
+bpy.utils.register_class(UEAPropertyGroup) 
+ 
+## create CollectionProperty and link it to the property class
+bpy.types.Object.myCollectionUEA = bpy.props.CollectionProperty(type = UEAPropertyGroup)
+bpy.types.Object.myCollectionUEA_index = bpy.props.IntProperty(min = -1, default = -1)
+
+## create operator to add or remove entries to/from  the Collection
+class OBJECT_OT_add_remove_Collection_Items_UE(bpy.types.Operator):
+    bl_label = "Add or Remove"
+    bl_idname = "collection.add_remove_ueactions"
+    __doc__ = """Button for Add, Remove, Refresh Action Set(s) list."""
+    set = bpy.props.StringProperty()
+ 
+    def invoke(self, context, event):
+        obj = context.object
+        collection = obj.myCollectionUEA
+        if self.set == "remove":
+            print("remove")
+            index = obj.myCollectionUEA_index
+            collection.remove(index)       # This remove on item in the collection list function of index value
+        if self.set == "add":
+            print("add")
+            added = collection.add()        # This add at the end of the collection list
+            added.name = "Action"+ str(random.randrange(0, 101, 2))
+        if self.set == "refresh":
+            print("refresh")
+            ArmatureSelect = None
+            ActionNames = []
+            BoneNames = []
+            for obj in bpy.data.objects:
+                if obj.type == 'ARMATURE' and obj.select == True:
+                    print("Armature Name:",obj.name)
+                    ArmatureSelect = obj
+                    for bone in obj.pose.bones:
+                        BoneNames.append(bone.name)
+                    break
+            actionsetmatchcount = 0	
+            for ActionNLA in bpy.data.actions:
+                nobone = 0
+                for group in ActionNLA.groups:	
+                    for abone in BoneNames:
+                        if abone == group.name:
+                            nobone += 1
+                            break
+                    if (len(ActionNLA.groups) == len(BoneNames)) and (nobone == len(ActionNLA.groups)):
+                        actionsetmatchcount += 1
+                        ActionNames.append(ActionNLA.name)
+            #print(dir(collection))
+            #print("collection:",len(collection))
+            print("action list check")
+            for action in ActionNames:
+                BfoundAction = False
+                #print("action:",action)
+                for c in collection:
+                    #print(c.name)
+                    if c.name == action:
+                        BfoundAction = True
+                        break
+                if BfoundAction == False:
+                    added = collection.add()        # This add at the end of the collection list
+                    added.name = action
+        #print("finish...")
+        return {'FINISHED'} 
 
 class ExportUDKAnimData(bpy.types.Operator):
     global exportmessage
@@ -1877,78 +1946,38 @@ class VIEW3D_PT_unrealtools_objectmode(bpy.types.Panel):
         layout = self.layout
         rd = context.scene
         layout.prop(rd, "unrealexport_settings",expand=True)        
-        #layout.operator("object.UnrealExport")#button#blender 2.55 version
-        layout.operator(OBJECT_OT_UnrealExport.bl_idname)#button blender #2.56 version
-        #print("Button Name:",OBJECT_OT_UnrealExport.bl_idname) #2.56 version
+        layout.operator(OBJECT_OT_UnrealExport.bl_idname)
         #FPS #it use the real data from your scene
         layout.prop(rd.render, "fps")
         
-        layout.prop(rd, "unrealactionexportall")
+        layout.prop(rd, "UEActionSetSettings")
         layout.prop(rd, "unrealdisplayactionsets")
-        #print("unrealdisplayactionsets:",rd.unrealdisplayactionsets)
-        if rd.unrealdisplayactionsets:
-            layout.label(text="Total Action Set(s):" + str(len(bpy.data.actions)))
-		    #armature data
-            amatureobject = None
-            bonenames = [] #bone name of the armature bones list 
-            #layout.label(text="object(s):" + str(len(bpy.data.objects)))
-            
-            for obj in bpy.data.objects:
+        
+        ArmatureSelect = None
+        for obj in bpy.data.objects:
                 if obj.type == 'ARMATURE' and obj.select == True:
-                    #print(dir(obj))
-                    amatureobject = obj
+                    #print("Armature Name:",obj.name)
+                    ArmatureSelect = obj
                     break
-                elif obj.type == 'ARMATURE':
-                    amatureobject = obj
-        
-            if amatureobject != None:
-                layout.label(text="Armature: " + amatureobject.name)
-                #print("Armature:",amatureobject.name)
-                boxactionset = layout.box()
-                for bone in amatureobject.pose.bones:
-                    bonenames.append(bone.name)
-                actionsetmatchcount = 0	
-                for ActionNLA in bpy.data.actions:
-                    nobone = 0
-                    for group in ActionNLA.groups:	
-                        for abone in bonenames:
-                            #print("name:>>",abone)
-                            if abone == group.name:
-                                nobone += 1
-                                break
-                    if (len(ActionNLA.groups) == len(bonenames)) and (nobone == len(ActionNLA.groups)):
-                        actionsetmatchcount += 1
-                        #print("Action Set match: Pass")
-                        boxactionset.label(text="Action Name: " + ActionNLA.name)
-                layout.label(text="Match Found: " + str(actionsetmatchcount))
-        
+        #display armature actions list
+        if ArmatureSelect != None and rd.unrealdisplayactionsets == True:
+            layout.label(("Selected: "+ArmatureSelect.name))
+            row = layout.row()
+            row.template_list(obj, "myCollectionUEA", obj, "myCollectionUEA_index")                        # This show list for the collection
+            col = row.column(align=True)
+            col.operator("collection.add_remove_ueactions", icon="ZOOMIN", text="").set = "add"            # This show a plus sign button
+            col.operator("collection.add_remove_ueactions", icon="ZOOMOUT", text="").set = "remove"        # This show a minus sign button        
+            col.operator("collection.add_remove_ueactions", icon="FILE_REFRESH", text="").set = "refresh"  # This show a refresh sign button
+            
+            ##change name of Entry:
+            if obj.myCollectionUEA:
+                entry = obj.myCollectionUEA[obj.myCollectionUEA_index]
+                layout.prop(entry, "name")
+                layout.prop(entry, "mybool")
         layout.operator(OBJECT_OT_UTSelectedFaceSmooth.bl_idname)        
         layout.operator(OBJECT_OT_UTRebuildArmature.bl_idname)
         layout.operator(OBJECT_OT_UTRebuildMesh.bl_idname)
-        
-        
-        
-        
-        
-        
-        #row = layout.row()
-        #row.label(text="Action Set(s)(not build)")
-        #for action in  bpy.data.actions:
-            #print(dir( action))
-            #print(action.frame_range)
-            #row = layout.row()
-            #row.prop(action, "name")
-            
-            #print(dir(action.groups[0]))
-            #for g in action.groups:#those are bones
-                #print("group...")
-                #print(dir(g))
-                #print("////////////")
-                #print((g.name))
-                #print("////////////")
-            
-            #row.label(text="Active:" + action.select)
-        btrimesh = False
+        layout.operator(OBJECT_OT_ToggleConsle.bl_idname)
         
 class OBJECT_OT_UnrealExport(bpy.types.Operator):
     global exportmessage
@@ -1977,6 +2006,17 @@ class OBJECT_OT_UnrealExport(bpy.types.Operator):
         self.report({'INFO'}, exportmessage)
         return{'FINISHED'}   
 
+class OBJECT_OT_ToggleConsle(bpy.types.Operator):
+    global exportmessage
+    bl_idname = "object.toggleconsle"  # XXX, name???
+    bl_label = "Toggle Console"
+    __doc__ = "Show or Hide Console."
+    
+    def invoke(self, context, event):
+        print("Init Export Script:")
+        bpy.ops.wm.console_toggle()
+        return{'FINISHED'} 
+
 class OBJECT_OT_UTSelectedFaceSmooth(bpy.types.Operator):
     bl_idname = "object.utselectfacesmooth"  # XXX, name???
     bl_label = "Select Smooth faces"
-- 
GitLab