Skip to content
Snippets Groups Projects
io_export_unreal_psk_psa.py 107 KiB
Newer Older
  • Learn to ignore specific revisions
  •     bpy.ops.object.mode_set(mode='OBJECT')
    
        bpy.context.scene.udk_option_triangulate = True
    
        me_ob.data = me_ob.to_mesh(bpy.context.scene, True, 'PREVIEW')  # write data 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
    
    
        if len(selectedobjects) > 1:
    
            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)):
    
                if count == 0:
                    bpy.context.scene.objects.active = cloneobjects[count]
    
                    print("Set Active Object:", cloneobjects[count].name)
    
                cloneobjects[count].select = True
    
            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.
    
    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:
    
                centermesh.append(selectmesh[countm])
    
            else:  # if not add here for not center
    
                notcentermesh.append(selectmesh[countm])
        selectmesh = []
    
        # 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
    
            return meshmerge(selectmesh)  # return merge object mesh
    
    
    
    
    
    # ===========================================================================
    
    # ===========================================================================
    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
    
        setmesh = mesh
        mesh = triangulate_mesh(mesh)
    
    
        if bpy.types.Scene.udk_copy_merge is True:
    
            bpy.context.scene.objects.unlink(setmesh)
    
    
        # print("FACES----:",len(mesh.data.tessfaces))
    
        verbose("Working mesh object: {}".format(mesh.name))
    
        # collect a list of the material names
    
        mat_slot_index = 0
    
        for slot in mesh.material_slots:
    
            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)
    
            v_material.MaterialName = slot.name
            v_material.TextureIndex = 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
    
        # 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 = {}
    
    John Phan's avatar
    John Phan committed
        sys.setrecursionlimit(1000000)
    
        smoothgroup_list = parse_smooth_groups(mesh.data)
    
        print("{} faces".format(len(mesh.data.tessfaces)))
    
        print("Smooth groups active:", bpy.context.scene.udk_option_smoothing_groups)
    
            for smooth_group in smoothgroup_list:
                if smooth_group.contains_face(face):
                    smoothgroup_id = smooth_group.id
                    break
    
            # 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
    
            #   conversion, and ends up creating faces that have only TWO points -
    
            #   one of the points is simply in the vertex list for the face twice.
            #   This is bad, since we can't get a real face normal for a LINE, we need
            #   a plane for this. So, before we add the face to the list of real faces,
            #   ensure that the face is actually a plane, and not a line. If it is not
    
            #   planar, just discard it and notify the user in the console after we're
            #   done dumping the rest of the faces
    
                # get or create the current material
    
                face_index = face.index
                has_uv = False
                face_uv = None
    
                    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]))
    
                    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
    
                    # 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
    
                    # clamp UV coords if udk_option_clight_uv is True
                    if bpy.context.scene.udk_option_clight_uv:
    
                        if (uv[0] > 1):
                            uv[0] = 1
                        if (uv[0] < 0):
                            uv[0] = 0
                        if (uv[1] > 1):
                            uv[1] = 1
                        if (uv[1] < 0):
                            uv[1] = 0
    
                    # RE - Append untransformed vector (for normal calc below)
                    # TODO: convert to Blender.Mathutils
    
                    vect_list.append(FVector(vert.co.x, vert.co.y, vert.co.z))
    
                    # vpos = vert.co * object_material_index
    
                    vpos = mesh.matrix_local * vert.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 = 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?
    
                    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]):
                            points_linked[lPoint].append(p)
                    else:
                        points_linked[lPoint] = [p]
    
                    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(
    
                # TODO: convert to Blender.Mathutils
                # get normal from blender
                no = face.normal
                # convert to FVector
                norm = FVector(no[0], no[1], no[2])
                # Calculate the normal of the face in blender order
                tnorm = vect_list[1].sub(vect_list[0]).cross(vect_list[2].sub(vect_list[1]))
                # RE - dot the normal from blender order against the blender normal
                # this gives the product of the two vectors' lengths along the blender normal axis
                # all that matters is the sign
                dot = norm.dot(tnorm)
    
                tri = VTriangle()
                # RE - magic: if the dot product above > 0, order the vertices 2, 1, 0
                #      if the dot product above < 0, order the vertices 0, 1, 2
                #      if the dot product is 0, then blender's normal is coplanar with the face
                #      and we cannot deduce which side of the face is the outside of the mesh
                if dot > 0:
                    (tri.WedgeIndex2, tri.WedgeIndex1, tri.WedgeIndex0) = wedge_list
                elif dot < 0:
                    (tri.WedgeIndex0, tri.WedgeIndex1, tri.WedgeIndex2) = wedge_list
                else:
    
                    dindex0 = face.vertices[0]
                    dindex1 = face.vertices[1]
                    dindex2 = face.vertices[2]
    
                    mesh.data.vertices[dindex0].select = True
                    mesh.data.vertices[dindex1].select = True
                    mesh.data.vertices[dindex2].select = True
    
                    raise Error("Normal coplanar with face! points: %s, %s, %s" % (str(mesh.data.vertices[dindex0].co),
                                                                                   str(mesh.data.vertices[dindex1].co),
                                                                                   str(mesh.data.vertices[dindex2].co)))
    
                    tri.SmoothingGroups = 1
                else:
                    tri.SmoothingGroups = 0
                tri.MatIndex = object_material_index
    
                if bpy.context.scene.udk_option_smoothing_groups:
                    tri.SmoothingGroups = smoothgroup_id
    
                    print("Bool Smooth")
    
            # END if not is_1d_face(current_face, mesh.data)
    
        # END face in mesh.data.faces
    
        print("{} points".format(len(points.dict)))
    
        for point in points.items():
            psk.AddPoint(point)
    
            raise Error("Mesh vertex limit exceeded! {} > 32767".format(len(points.dict)))
    
        print("{} wedges".format(len(wedges.dict)))
    
        for wedge in wedges.items():
            psk.AddWedge(wedge)
    
        # alert the user to degenerate face issues
        if discarded_face_count > 0:
            print("WARNING: Mesh contained degenerate faces (non-planar)")
            print("      Discarded {} faces".format(discarded_face_count))
    
        # RG - walk through the vertex groups and find the indexes into the PSK points array
        # for them, then store that index and the weight as a tuple in a new list of
        # 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
    
        # all vertex groups of the mesh (obj)...
        for obj_vertex_group in mesh.vertex_groups:
    
            # print("  bone group build:",obj_vertex_group.name)#print bone name
            # print(dir(obj_vertex_group))
    
            verbose("obj_vertex_group.name={}".format(obj_vertex_group.name))
    
            # all vertices in the mesh...
            for vertex in mesh.data.vertices:
    
                # 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
    
    
                        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
    
                            for point in points_linked[p]:
    
                                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
    
                            print("Error link points!")
                            pass
    
            # bone name, [point id and wieght]
            # print("Add Vertex Group:",obj_vertex_group.name, " No. Points:",len(vertex_list))
    
            psk.VertexGroups[obj_vertex_group.name] = vertex_list
    
        # remove the temporary triangulated mesh
    
        if bpy.context.scene.udk_option_triangulate 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):
    
        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 is True]
    
        # should be a single, unambiguous result
        if len(root_candidates) == 0:
            raise Error("Cannot find root for UDK bones. The root bone must use deform.")
    
        if len(root_candidates) > 1:
            raise Error("Ambiguous root for UDK. More than one root bone is using deform.")
    
        udk_root_bone = root_candidates[0]
        udk_bones = []
        BoneUtil.static_bone_id = 0  # replaces global
    
        # traverse bone chain
        print("{: <3} {: <48} {: <20}".format("ID", "Bone", "Status"))
        print()
        recurse_bone(udk_root_bone, udk_bones, psk, psa, 0, armature.matrix_local)
    
        # final validation
        if len(udk_bones) < 3:
            raise Error("Less than three bones may crash UDK (legacy issue?)")
    
        # return a list of bones making up the entire udk skel
        # this is passed to parse_animation instead of working from keyed bones in the action
        return udk_bones
    
    
    # ===========================================================================
    
    # bone              current bone
    # bones             bone list
    # psk               the PSK file object
    # psa               the PSA file object
    
    # indent            text indent for recursive log
    
    # ===========================================================================
    def recurse_bone(bone, bones, psk, psa, parent_id, parent_matrix, indent=""):
    
        if bone.parent is not None:
    
            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
            translation = (parent_tail - parent_head) + bone.head
    
        # 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?
    
        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
    
        BoneUtil.static_bone_id += 1            # ALT VERS
    
        psk.AddBone(make_vbone(bone.name, parent_id, child_count, quat, translation))
        psa.StoreBone(make_namedbonebinary(bone.name, parent_id, child_count, quat, translation, 1))
    
        # RG - dump influences for this bone - use the data we collected
        # in the mesh dump phase to map our bones to vertex groups
    
        if bone.name in psk.VertexGroups:
            vertex_list = psk.VertexGroups[bone.name]
    
            # print("vertex list:", len(vertex_list), " of >" ,bone.name)
    
    
                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?
    
        print("{:<3} {:<48} {:<20}".format(bone_id, indent + bone.name, status))
    
        # bone.matrix_local
        # recursively dump child bones
    
            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
    
    
    # ===========================================================================
    
    # 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):
    
        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)\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?
        # 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...
        for action in actions_to_export:
    
            # removed: check for armature with no animation; all it did was force you to add one
    
            if not len(action.fcurves):
                print("{} has no keys, skipping".format(action.name))
                continue
    
    John Phan's avatar
    John Phan committed
    
    
            # apply action to armature and update scene
    
            # 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()
    
            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)
    
            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))
    
            # build a list of pose bones relevant to the collated udk_bones
            # fixme: could be done once, prior to loop?
            udk_pose_bones = []
            for b in udk_bones:
                for pb in armature.pose.bones:
                    if b.name == pb.name:
                        udk_pose_bones.append(pb)
    
    
            # sort in the order the bones appear in the PSA file
            ordered_bones = {}
            ordered_bones = sorted([(psa.UseBone(b.name), b) for b in udk_pose_bones], key=operator.itemgetter(0))
    
            # NOTE: posebone.bone references the obj/edit bone
            # REMOVED: unique_bone_indexes is redundant?
    
            # frame loop...
            for i in range(frame_count):
    
                # verbose("FRAME {}".format(i), i) # test loop sampling
    
                # advance to frame (automatically updates the pose)
                context.scene.frame_set(frame)
    
                # compute the key for each bone
                for bone_data in ordered_bones:
    
                    bone_index = bone_data[0]
                    pose_bone = bone_data[1]
                    pose_bone_matrix = mathutils.Matrix(pose_bone.matrix)
    
                    if pose_bone.parent is not None:
    
                        pose_bone_parent_matrix = mathutils.Matrix(pose_bone.parent.matrix)
    
                        pose_bone_matrix = pose_bone_parent_matrix.inverted() * pose_bone_matrix
    
                    head = pose_bone_matrix.to_translation()
                    quat = pose_bone_matrix.to_quaternion().normalized()
    
                    if pose_bone.parent is not None:
    
                        quat = make_fquat(quat)
                    else:
                        quat = make_fquat_default(quat)
    
                    # scale animation position here?
    
                    if bpy.context.scene.udk_option_scale < 0 or bpy.context.scene.udk_option_scale > 1:
                        head.x = head.x * bpy.context.scene.udk_option_scale
                        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.Time = 1.0 / anim_rate  # according to C++ header this is "disregarded"
    
                # END for bone_data in ordered_bones
    
                raw_frame_index += 1
    
            # 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))
    
        # END for action in actions
    
        # restore
        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
    
    # ===========================================================================
    
        verbose(header("collate_actions"))
        actions_to_export = []
    
            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 is True and actionlist.bexport is True:
    
                        print("Added Action Set:", action.name)
    
                if bready is False:  # don't export it
                    print("Skipping Action Set:", action.name)
    
            verbose(" + {}".format(action.name))  # action set name
            actions_to_export.append(action)  # add to the action array
    
    
    # ===========================================================================
    
    # 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
    
        # bpy.ops.object.mode_set(mode='OBJECT')
    
        if bpy.context.scene.udk_option_selectobjects:  # if checked select object true do list object on export
    
            print("select mode:")
            if len(bpy.context.scene.udkArm_list) > 0:
    
                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
                        break
            else:
                raise Error("There is no Armature in the list!")
            meshselected = []
    
            # 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:
    
                if obj.type == 'MESH':
    
                    # print("PARENT MESH:",obj.name)
    
                    for udkmeshlist in bpy.context.scene.udkmesh_list:
    
                        if obj.name == udkmeshlist.name and udkmeshlist.bexport is True:
    
                    if bexportmesh is True:
                        print("Mesh Name:", obj.name, " < SELECT TO EXPORT!")
    
    
            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:
                    mesh = active_object
                else:
                    raise Error("The selected mesh is not parented to the armature")
    
            # otherwise, expect a single mesh parented to the armature (other object types are ignored)
            else:
    
                print("Number of meshes:", len(meshes))
                print("Number of meshes (selected):", len(meshes))
    
                if len(meshes) == 1:
                    mesh = meshes[0]
    
                elif len(meshes) > 1:
    
                    if len(meshselected) >= 1:
                        mesh = sortmesh(meshselected)
                    else:
                        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
    
            print("normal mode:")
            # try the active object
            if active_object and active_object.type == 'ARMATURE':
                armature = active_object
                bpy.ops.object.mode_set(mode='OBJECT')
            # otherwise, try for a single armature in the scene
            else:
    
                # bpy.ops.object.mode_set(mode='OBJECT')
    
                all_armatures = [obj for obj in bpy.context.scene.objects if obj.type == 'ARMATURE']
    
                if len(all_armatures) == 1:  # if armature has one scene just assign it
    
                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
    
                        raise Error("Please select an armatures in the scene")
    
                else:
                    raise Error("No armatures in scene")
    
            verbose("Found armature: {}".format(armature.name))
    
            meshselected = []
            parented_meshes = [obj for obj in armature.children if obj.type == 'MESH']
    
            if len(armature.children) == 0:
                raise Error("The selected Armature has no mesh parented to the Armature Object!")
    
                # 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:
                if active_object.parent == armature:
                    mesh = active_object
                else:
                    raise Error("The selected mesh is not parented to the armature")
    
            # otherwise, expect a single mesh parented to the armature (other object types are ignored)
            else:
    
                print("Number of meshes:", len(parented_meshes))
                print("Number of meshes (selected):", len(meshselected))
    
                if len(parented_meshes) == 1:
                    mesh = parented_meshes[0]
    
                elif len(parented_meshes) > 1:
                    if len(meshselected) >= 1:
                        mesh = sortmesh(meshselected)
                    else:
                        raise Error("More than one mesh(s) parented to armature. Select object(s)!")
                else:
                    raise Error("No mesh parented to armature")
    
            verbose("Found mesh: {}".format(mesh.name))
    
        if mesh 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 bpy.context.scene.udk_option_rebuildobjects:
    
            # print("INIT... REBUILDING...")
    
            # if deform mesh
            # rebuild the armature to raw. If there IK constraint it will ignore it
            armature = rebuildarmature(armature)
    
            mesh = rebuildmesh(mesh)  # rebuild the mesh to raw data format.
    
    
    # ===========================================================================
    
    # Returns a list of vertex groups in the mesh. Can be modified to filter
    # groups as necessary.
    # UNUSED
    
    # ===========================================================================
    def collate_vertex_groups(mesh):
    
        verbose("collate_vertex_groups")
        groups = []
    
            groups.append(group)
            verbose("  " + group.name)
    
    
    # ===========================================================================
    
    # ===========================================================================
    
        bpy.types.Scene.udk_copy_merge = False  # in case fail to export set this to default
        t = time.clock()
    
        print("Blender Version {}.{}.{}".format(bpy.app.version[0], bpy.app.version[1], bpy.app.version[2]))
        print("Filepath: {}".format(filepath))
    
        verbose("PSK={}, PSA={}".format(context.scene.udk_option_export_psk, context.scene.udk_option_export_psa))
    
        # find armature and mesh
        # [change this to implement alternative methods; raise Error() if not found]
        udk_armature, udk_mesh = find_armature_and_mesh()
    
        # check misc conditions
        if not (udk_armature.scale.x == udk_armature.scale.y == udk_armature.scale.z == 1):
            raise Error("bad armature scale: armature object should have uniform scale of 1 (ALT-S)")
    
        if not (udk_mesh.scale.x == udk_mesh.scale.y == udk_mesh.scale.z == 1):
            raise Error("bad mesh scale: mesh object should have uniform scale of 1 (ALT-S)")
    
        if not (udk_armature.location.x == udk_armature.location.y == udk_armature.location.z == 0):
            raise Error("bad armature location: armature should be located at origin (ALT-G)")
    
        if not (udk_mesh.location.x == udk_mesh.location.y == udk_mesh.location.z == 0):
            raise Error("bad mesh location: mesh should be located at origin (ALT-G)")
    
        # step 2
        udk_bones = parse_armature(udk_armature, psk, psa)
    
        if context.scene.udk_option_export_psa is True:
    
            actions = collate_actions()
            parse_animation(udk_armature, udk_bones, actions, psa)
    
        # write files
        print(header("Exporting", 'CENTER'))
    
        psk_filename = filepath + '.psk'
        psa_filename = filepath + '.psa'
    
        if context.scene.udk_option_export_psk is True:
    
            print("Skeletal mesh data...")
            psk.PrintOut()
    
            print("Exported: " + psk_filename)
            print()
    
        if context.scene.udk_option_export_psa is True:
    
            print("Animation data...")
            if not psa.IsEmpty():
                psa.PrintOut()
    
                print("Exported: " + psa_filename)
            else:
                print("No Animation (.psa file) to export")
    
            print()
    
        # if objects are rebuild do the unlink
    
        if bpy.context.scene.udk_option_rebuildobjects:
            print("Unlinking Objects")
    
            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)))
    
    
    # ===========================================================================
    
    # ===========================================================================
    class Operator_UDKExport(Operator):
    
        bl_idname = "object.udk_export"
        bl_label = "Export now"
    
            scene.udk_option_export_psk = (scene.udk_option_export == '0' or scene.udk_option_export == '2')
            scene.udk_option_export_psa = (scene.udk_option_export == '1' or scene.udk_option_export == '2')
    
            # cache settings
            restore_frame = scene.frame_current
    
            message = "Object(s) exported to: {}".format(filepath)
    
            try:
                export(filepath)
    
            except Error as err:
                print(err.message)
                message = err.message
    
            # restore settings
            scene.frame_set(restore_frame)
    
            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)
    
    
    # ===========================================================================
    
    # ===========================================================================
    class Operator_ToggleConsole(Operator):
    
        bl_idname = "object.toggle_console"
        bl_label = "Toggle console"
    
        def execute(self, context):
            bpy.ops.wm.console_toggle()
            return {'FINISHED'}
    
    
    # ===========================================================================
    
    # ===========================================================================
    
        if bpy.context.scene.udk_option_filename_src == '0':