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