diff --git a/blender_for_unrealengine.py b/blender_for_unrealengine.py
index 83f7576e..874662b8 100644
--- a/blender_for_unrealengine.py
+++ b/blender_for_unrealengine.py
@@ -1,576 +1,647 @@
-#====================== 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
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-# All rights reserved.
-#
-#======================= END GPL LICENSE BLOCK =============================
-
-# ----------------------------------------------
-# This addons allows to easily export several objects at the same time in .fbx
-# for use in unreal engine 4 by removing the usual constraints
-# while respecting UE4 naming conventions and a clean tree structure.
-# It also contains a small toolkit for collisions and sockets
-# ----------------------------------------------
-
-bl_info = {
- 'name': 'Blender for UnrealEngine',
- 'description': "This add-ons allows to easily export several "
- "objects at the same time for use in unreal engine 4.",
- 'author': 'Loux Xavier (BleuRaven)',
- 'version': (0, 1, 3),
- 'blender': (2, 79, 0),
- 'location': 'View3D > Tool > Unreal Engine 4',
- 'warning': '',
- "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
- "Scripts/Import-Export/Blender_For_UnrealEngine",
- 'tracker_url': '',
- 'support': 'COMMUNITY',
- 'category': 'Import-Export'}
-
-import os
-from mathutils import Vector
-from mathutils import Quaternion
-import bpy
-import fnmatch
-from bpy.props import *
-from bpy.types import Operator
-import math
-import mathutils
-from bpy import data as bpy_data
-
-#############################[Variables]#############################
-
-
-exportedAssets = [] #List of exported objects [Assetsype , ExportPath] Reset with each export
-
-
-#############################[Functions]#############################
-
-
-def GetStringSceneProperty(properties): #Allows to get StringSceneProperty with properties name.
- prop = ""
- try:
- prop = bpy.context.scene[properties]
- return prop
- except:
- pass
- return prop
-
-
-def ChecksRelationship(arrayA, arrayB): #Checks if it exits an identical variable in two lists
- for a in arrayA:
- for b in arrayB:
- if a == b:
- return True
- return False
-
-
-def SelectParentAndDesiredChilds(obj): #Selects only all child objects that must be exported with parent objet
- bpy.ops.object.select_all(action='DESELECT')
- bpy.context.scene.objects.active = obj
- bpy.ops.object.select_grouped(type='CHILDREN_RECURSIVE')
-
- for unwantedObj in FindAllObjetsByExportType("dont_export"): #Deselect all objects that should not be exported
- unwantedObj.select = False
- obj.select = True
-
-
-def ResetArmaturePose(obj): #Reset armature pose
- for x in obj.pose.bones:
- x.rotation_quaternion = Quaternion((0,0,0),0)
- x.scale = Vector((1,1,1))
- x.location = Vector((0,0,0))
-
-
-def VerifiDirs(directory): #check and create a folder if it does not exist
- if not os.path.exists(directory):
- os.makedirs(directory)
-
-print (max(0, 1))
-def ExportSingleAnimation(obj, targetAction, dirpath, filename): #Export a single animation
- if obj.type == 'ARMATURE':
- bpy.ops.object.mode_set(mode = 'OBJECT')
- originalLoc = Vector((0,0,0))
- originalLoc = originalLoc + obj.location #Save objet location
- obj.location = (0,0,0) #Moves object to the center of the scene for export
-
- SelectParentAndDesiredChilds(obj)
- ResetArmaturePose(obj)
-
- obj.animation_data.action = targetAction #Apply desired action
- keyframes = []
- for fcu in obj.animation_data.action.fcurves:
- for keyframe in fcu.keyframe_points:
- xCurve, yCurve = keyframe.co
- keyframes.append(xCurve)
- bpy.context.scene.frame_start = 0
- bpy.context.scene.frame_end = max(keyframes[-1],1) #Set end_frame on the final key the current action
- VerifiDirs(dirpath)
- fullpath = os.path.join( dirpath , filename )
- bpy.ops.export_scene.fbx(
- filepath=fullpath,
- check_existing=False,
- version='BIN7400',
- use_selection=True,
- object_types={'ARMATURE'},
- bake_anim=True,
- bake_anim_use_nla_strips=False,
- bake_anim_use_all_actions=False,
- bake_anim_force_startend_keying=True,
- )
- exportedAssets.append(["Animation", fullpath])
- obj.location = originalLoc #Move object to this saved location
-
-
-def ExportSingleMesh(obj, dirpath, filename): #Export a single Mesh
- if bpy.context.mode != 'OBJECT':
- bpy.ops.object.mode_set(mode = 'OBJECT')
- originalLoc = Vector((0,0,0))
- originalLoc = originalLoc + obj.location #Save objet location
- obj.location = (0,0,0) #Moves object to the center of the scene for export
-
- SelectParentAndDesiredChilds(obj)
-
- VerifiDirs(dirpath)
- fullpath = os.path.join( dirpath , filename )
- bpy.ops.export_scene.fbx(filepath=fullpath,
- check_existing=False,
- version='BIN7400',
- use_selection=True,
- bake_anim=False,
- )
- meshType = "StaticMesh"
- if obj.type == 'ARMATURE':
- meshType = "SkeletalMesh"
- exportedAssets.append([meshType , fullpath])
- obj.location = originalLoc #Move object to this saved location
-
-
-def FindAllObjetsByExportType(exportType): #Find all objets with a ExportEnum property desired
- targetObj = []
- for obj in bpy.context.scene.objects:
- try:
- prop = obj.ExportEnum
- if prop == exportType:
- targetObj.append(obj)
- except:
- pass
- return(targetObj)
-
-def GenerateUe4Name(name): #From a objet name generate a new name with by adding a suffix number
-
-
- def IsValidName(testedName): #Checks if an object uses this name. If not is a valid name
- for obj in bpy.context.scene.objects:
- if testedName == obj.name:
- return False
- return True
-
-
- valid = False
- number = 0
- newName = ""
- while valid == False:
- newName = name+"_"+str(number)
- if IsValidName(newName):
- valid = True
- else:
- number = number+1
- return newName
-
-
-def ConvertEmptyToUe4Socket(): #Convert all selected empty to unreal socket
- if CheckIfCollisionAndSocketOwnerIsValid():
- ownerObjName = GetStringSceneProperty("CollisionAndSocketOwner")
- bpy.ops.object.mode_set(mode = 'OBJECT')
- ownerObj = bpy.data.objects[ownerObjName]
- for obj in bpy.context.selected_objects:
- if obj != ownerObj:
- if obj.type == 'EMPTY':
- obj.name = GenerateUe4Name("SOCKET_"+ownerObjName)
- obj.scale = (0.01,0.01,0.01)
- obj.empty_draw_size = 100
- if obj.parent != ownerObj.name:
- bpy.ops.object.select_all(action='DESELECT')
- obj.select = True
- bpy.context.scene.objects.active = ownerObj
- bpy.ops.object.parent_set(type='OBJECT', keep_transform=True)
-
-
-def ConvertMeshToUe4Collision(collisionType): #Convert all selected mesh to unreal collisions
- ownerObjName = GetStringSceneProperty("CollisionAndSocketOwner")
- if CheckIfCollisionAndSocketOwnerIsValid():
- bpy.ops.object.mode_set(mode = 'OBJECT')
- ownerObj = bpy.data.objects[ownerObjName]
- prefixName = ""
-
- #Set the name of the Prefix depending on the type of collision in agreement with unreal FBX Pipeline
- if collisionType == "Box":
- prefixName = "UBX_"
- elif collisionType == "Capsule":
- prefixName = "UCP_"
- elif collisionType == "Sphere":
- prefixName = "USP_"
- elif collisionType == "Convex":
- prefixName = "UCX_"
- else:
- return
-
- mat = bpy.data.materials.get("UE4Collision")
- if mat is None:
- mat = bpy.data.materials.new(name="UE4Collision")
- mat.diffuse_color = (0, 0.6, 0)
- mat.alpha = 0.1
- mat.use_transparency = True
-
- for obj in bpy.context.selected_objects:
- if obj != ownerObj:
- if obj.type == 'MESH':
- obj.data.materials.clear()
- obj.data.materials.append(mat)
- obj.name = GenerateUe4Name(prefixName+ownerObjName)
- obj.show_wire = True
- obj.show_transparent = True
- if obj.parent != ownerObj.name:
- bpy.ops.object.select_all(action='DESELECT')
- obj.select = True
- bpy.context.scene.objects.active = ownerObj
- bpy.ops.object.parent_set(type='OBJECT', keep_transform=True)
-
-
-def ExportAllByList(targetObjets): #Export all objects that need to be exported
- if len(targetObjets) > 0:
- Scene = bpy.context.scene
- blendFileLoc = os.path.dirname(bpy.data.filepath)
- smPrefix = GetStringSceneProperty("StaticPrefixExportName")
- skPrefix = GetStringSceneProperty("SkeletalPrefixExportName")
- animPrefix = GetStringSceneProperty("AnimPrefixExportName")
- for obj in targetObjets:
- if obj.type == 'ARMATURE':
- exportDir = os.path.join( blendFileLoc, "ExportedFbx" , "SkeletalMesh", obj.name )
- ExportSingleMesh(obj, exportDir, skPrefix+obj.name+".fbx")
- for Action in bpy.data.actions:
-
- objBonesName = [bone.name for bone in obj.pose.bones]
- animBonesName = []
- for curve in Action.fcurves:
- try:
- animBonesName.append(curve.data_path.split('"')[1])
- except:
- pass
-
- if ChecksRelationship(objBonesName, animBonesName):
- animExportDir = os.path.join( exportDir, "Anim" )
- ExportSingleAnimation(obj, Action, animExportDir, animPrefix+obj.name+"_"+Action.name+".fbx")
- else:
- exportDir = os.path.join( blendFileLoc, "ExportedFbx" , "StaticMesh" )
- ExportSingleMesh(obj, exportDir, smPrefix+obj.name+".fbx")
-
-def CorrectBadProperty():
- Scene = bpy.context.scene
- foo_objs1 = [obj for obj in Scene.objects if
- fnmatch.fnmatchcase(obj.name, "UBX*") or
- fnmatch.fnmatchcase(obj.name, "UCX*") or
- fnmatch.fnmatchcase(obj.name, "UCP*") or
- fnmatch.fnmatchcase(obj.name, "USP*") or
- fnmatch.fnmatchcase(obj.name, "SOCKET*")]
-
- for u in foo_objs1:
- try:
-
- if u.ExportEnum == "export_and_childs":
- u.ExportEnum = "auto"
- except:
- pass
-
-
-def ExportComplete(self): #Display a summary at the end of the export and reset "exportedAssets"
- if len(exportedAssets) > 0:
- self.report({'INFO'}, "Export of "+str(len(exportedAssets))+" asset(s) has been finalized ! ")
- self.report({'INFO'}, "Look in th console for more info.")
- print ("################## Exported asset(s) ##################")
- for asset in exportedAssets:
- print (asset[0]+" --> "+asset[1])
- print ("################## Exported asset(s) ##################")
- else:
- self.report({'WARNING'}, "Not found assets. with \"Export and child\" properties.")
- self.report({'OPERATOR'}, "Pleas select at least one object and set \"Export and child\" properties.")
- del exportedAssets[:]
-
-
-#############################[Visual and UI]#############################
-
-#### UI Function
-def CheckIfCollisionAndSocketOwnerIsValid():
- for obj in bpy.data.objects:
- if GetStringSceneProperty("CollisionAndSocketOwner") == obj.name:
- owner = bpy.data.objects[GetStringSceneProperty("CollisionAndSocketOwner")]
- if owner.type != "ARMATURE":
- return True
- return False
-
-#### Propertys
-def initObjectProperties():
- bpy.types.Object.ExportEnum = EnumProperty(
- name = "Type of export ",
- description = "Export type of active object",
- items = [("auto", "Auto", "Export only if one parents is \"Export and child\"", "KEY_HLT", 1),
- ("export_and_childs", "Export and childs", "Export self objet and all childs", "KEYINGSET", 2),
- ("dont_export", "Dont export", "Will never export", "KEY_DEHLT", 3)])
-
-
-def initSceneProperties():
- bpy.types.Scene.CollisionAndSocketOwner = StringProperty(
- name = "Owner",
- description = "Enter the owner name of the collision or socket",
- default = "")
-
- bpy.types.Scene.StaticPrefixExportName = StringProperty(
- name = "StaticMesh Prefix",
- description = "Prefix of staticMesh when exported",
- maxlen = 255,
- default = "SM_")
-
- bpy.types.Scene.SkeletalPrefixExportName = StringProperty(
- name = "SkeletalMesh Prefix ",
- description = "Prefix of SkeletalMesh when exported",
- maxlen = 255,
- default = "SK_")
-
- bpy.types.Scene.AnimPrefixExportName = StringProperty(
- name = "AnimationSequence Prefix",
- description = "Prefix of AnimationSequence when exported",
- maxlen = 255,
- default = "Anim_")
- return
-
-
-def ChecksProp(prop):
- try:
- value = prop["StaticPrefixExportName"]
- except:
- pass
- prop["StaticPrefixExportName"] = "SM_"
- try:
- value = prop["SkeletalPrefixExportName"]
- except:
- pass
- prop["SkeletalPrefixExportName"] = "SK_"
- try:
- value = prop["AnimPrefixExportName"]
- except:
- pass
- prop["AnimPrefixExportName"] = "Anim_"
-
- return
-
-#### Panels
-class ue4PropertiesPanel(bpy.types.Panel): #Is Objet Properties panel
- bl_idname = "panel.ue4.properties"
- bl_label = "Objet Properties"
- bl_space_type = "VIEW_3D"
- bl_region_type = "TOOLS"
- bl_category = "Unreal Engine 4"
-
- def draw(self, context):
- layout = self.layout
- try:
- ob = context.object
- layout.prop(ob, 'ExportEnum')
- except:
- pass
- row = self.layout.row().split(percentage = 0.80 )
- row = row.column()
-
- row.operator("object.selectexport")
- row.operator("object.deselectexport")
-
-
-class ue4CollisionsAndSocketsPanel(bpy.types.Panel): #Is Collisions And Sockets panel
- bl_idname = "panel.ue4.collisionsandsockets"
- bl_label = "Collisions And Sockets"
- bl_space_type = "VIEW_3D"
- bl_region_type = "TOOLS"
- bl_category = "Unreal Engine 4"
-
- def draw(self, context):
-
- scene = context.scene
- layout = self.layout
-
- ownerSelect = layout.row().split(align=True, percentage=0.9)
- ownerSelect.prop_search(scene, "CollisionAndSocketOwner", scene, "objects")
- ownerSelect.operator("object.setownerbyactive", text="", icon='EYEDROPPER')
-
-
-
- layout.label("Convert selected objet to Unreal collision or socket", icon='PHYSICS')
-
- convertButtons = layout.row().split(percentage = 0.80 )
- convertButtons.active = CheckIfCollisionAndSocketOwnerIsValid()
- convertButtons.enabled = CheckIfCollisionAndSocketOwnerIsValid()
- convertButtons = convertButtons.column()
- convertButtons.operator("object.converttoboxcollision", icon='MESH_CUBE')
- convertButtons.operator("object.converttoconvexcollision", icon='MESH_ICOSPHERE')
- convertButtons.operator("object.converttocapsulecollision", icon='MESH_CAPSULE')
- convertButtons.operator("object.converttospherecollision", icon='SOLID')
- convertButtons.operator("object.converttosocket", icon='OUTLINER_DATA_EMPTY')
-
-class ue4CheckCorrect(bpy.types.Panel): #Is Check and correct panel
- bl_idname = "panel.ue4.CheckCorrect"
- bl_label = "Check and correct"
- bl_space_type = "VIEW_3D"
- bl_region_type = "TOOLS"
- bl_category = "Unreal Engine 4"
-
- def draw(self, context):
- scn = context.scene
- props = self.layout.row().operator("object.correctproperty", icon='FILE_TICK')
-
-class ue4ExportPanel(bpy.types.Panel): #Is Export panel
- bl_idname = "panel.ue4.export"
- bl_label = "Export"
- bl_space_type = "VIEW_3D"
- bl_region_type = "TOOLS"
- bl_category = "Unreal Engine 4"
-
- def draw(self, context):
- scn = context.scene
- self.layout.prop(scn, 'StaticPrefixExportName', icon='OBJECT_DATA')
- self.layout.prop(scn, 'SkeletalPrefixExportName', icon='OBJECT_DATA')
- self.layout.prop(scn, 'AnimPrefixExportName', icon='OBJECT_DATA')
- props = self.layout.row().operator("object.exportforunreal", icon='EXPORT')
-
-#### Buttons
-class SelectExportAndChildButton(bpy.types.Operator):
- bl_label = "Select all \"Export and childs\" objects"
- bl_idname = "object.selectexport"
- bl_description = "Select all root objects that will be exported"
-
- def execute(self, context):
- for obj in FindAllObjetsByExportType("export_and_childs"):
- obj.select = True
- return {'FINISHED'}
-
-
-class DeselectExportAndChildButton(bpy.types.Operator):
- bl_label = "Deselect all \"Export and childs\" objects"
- bl_idname = "object.deselectexport"
- bl_description = "Deselect all root objects that will be exported"
-
- def execute(self, context):
- for obj in FindAllObjetsByExportType("export_and_childs"):
- obj.select = False
- return {'FINISHED'}
-
-class SetOwnerByActive(bpy.types.Operator):
- bl_label = "Set owner by active selection"
- bl_idname = "object.setownerbyactive"
- bl_description = "Set owner by active selection"
-
- def execute(self, context):
- try:
- bpy.context.scene["CollisionAndSocketOwner"] = bpy.context.active_object.name
- except:
- pass
- bpy.context.scene["CollisionAndSocketOwner"] = ""
- return {'FINISHED'}
-
-class ConvertToUECollisionButtonBox(bpy.types.Operator):
- bl_label = "Convert to box (UBX)"
- bl_idname = "object.converttoboxcollision"
- bl_description = "Convert selected mesh(s) to Unreal collision ready for export (Boxes type)"
-
- def execute(self, context):
- ConvertMeshToUe4Collision("Box")
- return {'FINISHED'}
-
-
-class ConvertToUECollisionButtonCapsule(bpy.types.Operator):
- bl_label = "Convert to capsule (UCP)"
- bl_idname = "object.converttocapsulecollision"
- bl_description = "Convert selected mesh(s) to Unreal collision ready for export (Capsules type)"
-
- def execute(self, context):
- ConvertMeshToUe4Collision("Capsule")
- return {'FINISHED'}
-
-
-class ConvertToUECollisionButtonSphere(bpy.types.Operator):
- bl_label = "Convert to sphere (USP)"
- bl_idname = "object.converttospherecollision"
- bl_description = "Convert selected mesh(s) to Unreal collision ready for export (Spheres type)"
-
- def execute(self, context):
- ConvertMeshToUe4Collision("Sphere")
- return {'FINISHED'}
-
-
-class ConvertToUECollisionButtonConvex(bpy.types.Operator):
- bl_label = "Convert to convex shape (UCX)"
- bl_idname = "object.converttoconvexcollision"
- bl_description = "Convert selected mesh(s) to Unreal collision ready for export (Convex shapes type)"
-
- def execute(self, context):
- ConvertMeshToUe4Collision("Convex")
- return {'FINISHED'}
-
-
-class ConvertToUESocketButton(bpy.types.Operator):
- bl_label = "Convert to socket (SOCKET)"
- bl_idname = "object.converttosocket"
- bl_description = "Convert selected empty(s) to Unreal sockets ready for export"
-
- def execute(self, context):
- ConvertEmptyToUe4Socket()
- return {'FINISHED'}
-
-
-class ExportForUnrealEngineButton(bpy.types.Operator):
- bl_label = "Export for UnrealEngine 4"
- bl_idname = "object.exportforunreal"
- bl_description = "Export all objet intended for export in scene to fbx"
-
- def execute(self, context):
- ChecksProp(bpy.context.scene)
- ExportAllByList(FindAllObjetsByExportType("export_and_childs"))
- ExportComplete(self)
- return {'FINISHED'}
-
-class CorrectBadPropertyButton(bpy.types.Operator):
- bl_label = "Correct bad property"
- bl_idname = "object.correctproperty"
- bl_description = "Corrects bad properties"
-
- def execute(self, context):
- CorrectBadProperty()
- return {'FINISHED'}
-
-
-#############################[...]#############################
-
-
-def register():
- bpy.utils.register_module(__name__)
- bpy.types.Scene.my_prop = bpy.props.StringProperty(default="default value")
- initObjectProperties()
- initSceneProperties()
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
-
-
-if __name__ == "__main__":
+#====================== 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
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+# All rights reserved.
+#
+#======================= END GPL LICENSE BLOCK =============================
+
+# ----------------------------------------------
+# This addons allows to easily export several objects at the same time in .fbx
+# for use in unreal engine 4 by removing the usual constraints
+# while respecting UE4 naming conventions and a clean tree structure.
+# It also contains a small toolkit for collisions and sockets
+# ----------------------------------------------
+
+bl_info = {
+ 'name': 'Blender for UnrealEngine',
+ 'description': "This add-ons allows to easily export several "
+ "objects at the same time for use in unreal engine 4.",
+ 'author': 'Loux Xavier (BleuRaven)',
+ 'version': (0, 1, 5),
+ 'blender': (2, 79, 0),
+ 'location': 'View3D > Tool > Unreal Engine 4',
+ 'warning': '',
+ "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
+ "Scripts/Import-Export/Blender_For_UnrealEngine",
+ 'tracker_url': '',
+ 'support': 'COMMUNITY',
+ 'category': 'Import-Export'}
+
+import os
+from mathutils import Vector
+from mathutils import Quaternion
+import bpy
+import fnmatch
+from bpy.props import *
+from bpy.types import Operator
+import math
+import mathutils
+from bpy import data as bpy_data
+
+#############################[Variables]#############################
+
+
+exportedAssets = [] #List of exported objects [Assetsype , ExportPath] Reset with each export
+
+
+#############################[Functions]#############################
+
+
+def GetStringSceneProperty(properties): #Allows to get StringSceneProperty with properties name.
+ prop = ""
+ try:
+ prop = bpy.context.scene[properties]
+ return prop
+ except:
+ pass
+ return prop
+
+
+def ChecksRelationship(arrayA, arrayB): #Checks if it exits an identical variable in two lists
+ for a in arrayA:
+ for b in arrayB:
+ if a == b:
+ return True
+ return False
+
+
+def SelectParentAndDesiredChilds(obj): #Selects only all child objects that must be exported with parent objet
+ bpy.ops.object.select_all(action='DESELECT')
+ bpy.context.scene.objects.active = obj
+ bpy.ops.object.select_grouped(type='CHILDREN_RECURSIVE')
+
+ for unwantedObj in FindAllObjetsByExportType("dont_export"): #Deselect all objects that should not be exported
+ unwantedObj.select = False
+ obj.select = True
+
+
+def ResetArmaturePose(obj): #Reset armature pose
+ for x in obj.pose.bones:
+ x.rotation_quaternion = Quaternion((0,0,0),0)
+ x.scale = Vector((1,1,1))
+ x.location = Vector((0,0,0))
+
+
+def VerifiDirs(directory): #check and create a folder if it does not exist
+ if not os.path.exists(directory):
+ os.makedirs(directory)
+
+
+def ExportSingleAnimation(obj, targetAction, dirpath, filename): #Export a single animation
+ if obj.type == 'ARMATURE':
+ UserAction = obj.animation_data.action #Save current action
+ bpy.ops.object.mode_set(mode = 'OBJECT')
+ originalLoc = Vector((0,0,0))
+ originalLoc = originalLoc + obj.location #Save objet location
+ obj.location = (0,0,0) #Moves object to the center of the scene for export
+
+ SelectParentAndDesiredChilds(obj)
+ ResetArmaturePose(obj)
+
+ obj.animation_data.action = targetAction #Apply desired action
+ keyframes = []
+ for fcu in obj.animation_data.action.fcurves:
+ for keyframe in fcu.keyframe_points:
+ xCurve, yCurve = keyframe.co
+ keyframes.append(xCurve)
+ bpy.context.scene.frame_start = 0
+ bpy.context.scene.frame_end = max(keyframes[-1],1) #Set end_frame on the final key the current action
+
+ absdirpath = bpy.path.abspath(dirpath)
+
+ VerifiDirs(absdirpath)
+ fullpath = os.path.join( absdirpath , filename )
+ bpy.ops.export_scene.fbx(
+ filepath=fullpath,
+ check_existing=False,
+ version='BIN7400',
+ use_selection=True,
+ object_types={'ARMATURE'},
+ bake_anim=True,
+ bake_anim_use_nla_strips=False,
+ bake_anim_use_all_actions=False,
+ bake_anim_force_startend_keying=True,
+ )
+ exportedAssets.append(["Animation", fullpath])
+ obj.location = originalLoc #Resets previous object location
+ ResetArmaturePose(obj)
+ obj.animation_data.action = UserAction #Resets previous action
+
+
+
+def ExportSingleMesh(obj, dirpath, filename): #Export a single Mesh
+
+ bpy.ops.object.mode_set(mode = 'OBJECT')
+ originalLoc = Vector((0,0,0))
+ originalLoc = originalLoc + obj.location #Save current object location
+
+ obj.location = (0,0,0) #Moves object to the center of the scene for export
+
+ SelectParentAndDesiredChilds(obj)
+
+ absdirpath = bpy.path.abspath(dirpath)
+ VerifiDirs(absdirpath)
+ fullpath = os.path.join( absdirpath , filename )
+ bpy.ops.export_scene.fbx(filepath=fullpath,
+ check_existing=False,
+ version='BIN7400',
+ use_selection=True,
+ bake_anim=False
+ )
+ meshType = "StaticMesh"
+ if obj.type == 'ARMATURE':
+ meshType = "SkeletalMesh"
+ exportedAssets.append([meshType , fullpath])
+
+ obj.location = originalLoc #Resets previous object location
+
+
+
+
+def FindAllObjetsByExportType(exportType): #Find all objets with a ExportEnum property desired
+ targetObj = []
+ for obj in bpy.context.scene.objects:
+ try:
+ prop = obj.ExportEnum
+ if prop == exportType:
+ targetObj.append(obj)
+ except:
+ pass
+ return(targetObj)
+
+
+def GenerateUe4Name(name): #From a objet name generate a new name with by adding a suffix number
+
+
+ def IsValidName(testedName): #Checks if an object uses this name. If not is a valid name
+ for obj in bpy.context.scene.objects:
+ if testedName == obj.name:
+ return False
+ return True
+
+
+ valid = False
+ number = 0
+ newName = ""
+ while valid == False:
+ newName = name+"_"+str(number)
+ if IsValidName(newName):
+ valid = True
+ else:
+ number = number+1
+ return newName
+
+
+def ConvertToUe4Socket(): #Convert all selected empty to unreal socket
+ if CheckIfCollisionAndSocketOwnerIsValid():
+ ownerObjName = GetStringSceneProperty("CollisionAndSocketOwner")
+
+ UserMode = bpy.context.object.mode #Save current mode
+ UserActive = bpy.context.active_object #Save current active object
+ UserSelected = bpy.context.selected_objects #Save current selected objects
+
+ bpy.ops.object.mode_set(mode = 'OBJECT')
+ ownerObj = bpy.data.objects[ownerObjName]
+ for obj in bpy.context.selected_objects:
+ if obj != ownerObj:
+ if obj.type == 'EMPTY':
+ obj.name = GenerateUe4Name("SOCKET_"+ownerObjName)
+ obj.scale = (0.01,0.01,0.01)
+ obj.empty_draw_size = 100
+ if obj.parent != ownerObj.name:
+ bpy.ops.object.select_all(action='DESELECT')
+ obj.select = True
+ bpy.context.scene.objects.active = ownerObj
+ bpy.ops.object.parent_set(type='OBJECT', keep_transform=True)
+
+ for obj in UserSelected: obj.select = True #Resets previous selected objects
+ bpy.context.scene.objects.active = UserActive #Resets previous active object
+ bpy.ops.object.mode_set(mode = UserMode) #Resets previous mode
+
+
+def CheckIfCollisionAndSocketOwnerIsValid():
+ for obj in bpy.data.objects:
+ if GetStringSceneProperty("CollisionAndSocketOwner") == obj.name:
+ owner = bpy.data.objects[GetStringSceneProperty("CollisionAndSocketOwner")]
+ if owner.type != "ARMATURE":
+ return True
+ return False
+
+
+def ConvertToUe4Collision(collisionType): #Convert all selected objets to unreal collisions
+ if CheckIfCollisionAndSocketOwnerIsValid():
+ ownerObjName = GetStringSceneProperty("CollisionAndSocketOwner")
+
+ UserMode = bpy.context.object.mode #Save current mode
+ UserActive = bpy.context.active_object #Save current active object
+ UserSelected = bpy.context.selected_objects #Save current selected objects
+
+ bpy.ops.object.mode_set(mode = 'OBJECT')
+ ownerObj = bpy.data.objects[ownerObjName]
+
+ #Set the name of the Prefix depending on the type of collision in agreement with unreal FBX Pipeline
+ prefixName = ""
+ if collisionType == "Box":
+ prefixName = "UBX_"
+ elif collisionType == "Capsule":
+ prefixName = "UCP_"
+ elif collisionType == "Sphere":
+ prefixName = "USP_"
+ elif collisionType == "Convex":
+ prefixName = "UCX_"
+ else:
+ return
+
+ mat = bpy.data.materials.get("UE4Collision")
+ if mat is None:
+ mat = bpy.data.materials.new(name="UE4Collision")
+ mat.diffuse_color = (0, 0.6, 0)
+ mat.alpha = 0.1
+ mat.use_transparency = True
+
+ for obj in bpy.context.selected_objects:
+ if obj != ownerObj:
+ if obj.type == 'MESH':
+ obj.data.materials.clear()
+ obj.data.materials.append(mat)
+ obj.name = GenerateUe4Name(prefixName+ownerObjName)
+ obj.show_wire = True
+ obj.show_transparent = True
+ if obj.parent != ownerObj.name:
+ bpy.ops.object.select_all(action='DESELECT')
+ obj.select = True
+ bpy.context.scene.objects.active = ownerObj
+ bpy.ops.object.parent_set(type='OBJECT', keep_transform=True)
+
+ for obj in UserSelected: obj.select = True #Resets previous selected objects
+ bpy.context.scene.objects.active = UserActive #Resets previous active object
+ bpy.ops.object.mode_set(mode = UserMode) #Resets previous mode
+
+
+def ExportAllByList(targetObjets): #Export all objects that need to be exported
+ if len(targetObjets) > 0:
+ UserMode = bpy.context.object.mode #Save current mode
+ UserActive = bpy.context.active_object #Save current active object
+ UserSelected = bpy.context.selected_objects #Save current selected objects
+
+ Scene = bpy.context.scene
+ blendFileLoc = os.path.dirname(bpy.data.filepath)
+ smPrefix = GetStringSceneProperty("StaticPrefixExportName")
+ skPrefix = GetStringSceneProperty("SkeletalPrefixExportName")
+ animPrefix = GetStringSceneProperty("AnimPrefixExportName")
+ for obj in targetObjets:
+ if obj.type == 'ARMATURE':
+ exportDir = os.path.join( GetStringSceneProperty("ExportNameFilePath") , "SkeletalMesh", obj.name )
+ ExportSingleMesh(obj, exportDir, skPrefix+obj.name+".fbx")
+
+ UserStartFrame = bpy.context.scene.frame_start #Save current start frame
+ UserEndFrame = bpy.context.scene.frame_end #Save current end frame
+
+ for Action in bpy.data.actions:
+
+ objBonesName = [bone.name for bone in obj.pose.bones]
+ animBonesName = []
+ for curve in Action.fcurves:
+ try:
+ animBonesName.append(curve.data_path.split('"')[1])
+ except:
+ pass
+
+ if ChecksRelationship(objBonesName, animBonesName):
+ animExportDir = os.path.join( exportDir, "Anim" )
+ ExportSingleAnimation(obj, Action, animExportDir, animPrefix+obj.name+"_"+Action.name+".fbx")
+
+ bpy.context.scene.frame_start = UserStartFrame #Resets previous start frame
+ bpy.context.scene.frame_end = UserEndFrame #Resets previous end frame
+
+ else:
+ exportDir = os.path.join( GetStringSceneProperty("ExportNameFilePath") , "StaticMesh" )
+ ExportSingleMesh(obj, exportDir, smPrefix+obj.name+".fbx")
+
+ bpy.ops.object.select_all(action='DESELECT')
+ for obj in UserSelected: obj.select = True #Resets previous active object
+ bpy.context.scene.objects.active = UserActive #Resets previous active object
+ bpy.ops.object.mode_set(mode = UserMode) #Resets previous mode
+
+
+def CorrectBadProperty():
+ Scene = bpy.context.scene
+ foo_objs1 = [obj for obj in Scene.objects if
+ fnmatch.fnmatchcase(obj.name, "UBX*") or
+ fnmatch.fnmatchcase(obj.name, "UCX*") or
+ fnmatch.fnmatchcase(obj.name, "UCP*") or
+ fnmatch.fnmatchcase(obj.name, "USP*") or
+ fnmatch.fnmatchcase(obj.name, "SOCKET*")]
+
+ for u in foo_objs1:
+ try:
+
+ if u.ExportEnum == "export_and_childs":
+ u.ExportEnum = "auto"
+ except:
+ pass
+
+
+def ExportComplete(self): #Display a summary at the end of the export and reset "exportedAssets"
+ if len(exportedAssets) > 0:
+ self.report({'INFO'}, "Export of "+str(len(exportedAssets))+" asset(s) has been finalized ! Look in th console for more info.")
+ print ("################## Exported asset(s) ##################")
+ for asset in exportedAssets:
+ print (asset[0]+" --> "+asset[1])
+ print ("################## Exported asset(s) ##################")
+ else:
+ self.report({'WARNING'}, "Not found assets. with \"Export and child\" properties.")
+ self.report({'OPERATOR'}, "Pleas select at least one object and set \"Export and child\" properties.")
+ del exportedAssets[:]
+
+
+#############################[Visual and UI]#############################
+
+#### Propertys
+def initObjectProperties():
+ bpy.types.Object.ExportEnum = EnumProperty(
+ name = "Type of export ",
+ description = "Export type of active object",
+ items = [("auto", "Auto", "Export only if one parents is \"Export and child\"", "KEY_HLT", 1),
+ ("export_and_childs", "Export and childs", "Export self objet and all childs", "KEYINGSET", 2),
+ ("dont_export", "Dont export", "Will never export", "KEY_DEHLT", 3)])
+
+
+def initSceneProperties():
+ bpy.types.Scene.CollisionAndSocketOwner = StringProperty(
+ name = "Owner",
+ description = "Enter the owner name of the collision or socket",
+ default = "")
+
+ bpy.types.Scene.StaticPrefixExportName = StringProperty(
+ name = "StaticMesh Prefix",
+ description = "Prefix of staticMesh when exported",
+ maxlen = 255,
+ default = "SM_")
+
+ bpy.types.Scene.SkeletalPrefixExportName = StringProperty(
+ name = "SkeletalMesh Prefix ",
+ description = "Prefix of SkeletalMesh when exported",
+ maxlen = 255,
+ default = "SK_")
+
+ bpy.types.Scene.AnimPrefixExportName = StringProperty(
+ name = "AnimationSequence Prefix",
+ description = "Prefix of AnimationSequence when exported",
+ maxlen = 255,
+ default = "Anim_")
+
+ bpy.types.Scene.ExportNameFilePath = StringProperty(
+ name = "Export file path",
+ description = "Choose a directory for export",
+ maxlen = 1024,
+ default = "//ExportedFbx\\",
+ subtype = 'DIR_PATH')
+
+ return
+
+
+def ChecksProp(prop):
+ try:
+ value = prop["StaticPrefixExportName"]
+ except:
+ pass
+ prop["StaticPrefixExportName"] = "SM_"
+ try:
+ value = prop["SkeletalPrefixExportName"]
+ except:
+ pass
+ prop["SkeletalPrefixExportName"] = "SK_"
+ try:
+ value = prop["AnimPrefixExportName"]
+ except:
+ pass
+ prop["AnimPrefixExportName"] = "Anim_"
+ try:
+ value = prop["ExportNameFilePath"]
+ except:
+ pass
+ prop["ExportNameFilePath"] = "//ExportedFbx\\"
+
+ return
+
+#### Panels
+class ue4PropertiesPanel(bpy.types.Panel): #Is Objet Properties panel
+ bl_idname = "panel.ue4.properties"
+ bl_label = "Objet Properties"
+ bl_space_type = "VIEW_3D"
+ bl_region_type = "TOOLS"
+ bl_category = "Unreal Engine 4"
+
+ def draw(self, context):
+ layout = self.layout
+ try:
+ ob = context.object
+ layout.prop(ob, 'ExportEnum')
+ except:
+ pass
+ row = self.layout.row().split(percentage = 0.80 )
+ row = row.column()
+
+ row.operator("object.selectexport")
+ row.operator("object.deselectexport")
+
+
+class ue4CollisionsAndSocketsPanel(bpy.types.Panel): #Is Collisions And Sockets panel
+ bl_idname = "panel.ue4.collisionsandsockets"
+ bl_label = "Collisions And Sockets"
+ bl_space_type = "VIEW_3D"
+ bl_region_type = "TOOLS"
+ bl_category = "Unreal Engine 4"
+
+ def draw(self, context):
+
+ scene = context.scene
+ layout = self.layout
+
+ ownerSelect = layout.row().split(align=True, percentage=0.9)
+ ownerSelect.prop_search(scene, "CollisionAndSocketOwner", scene, "objects")
+ ownerSelect.operator("object.setownerbyactive", text="", icon='EYEDROPPER')
+
+
+
+ layout.label("Convert selected objet to Unreal collision or socket", icon='PHYSICS')
+
+ convertButtons = layout.row().split(percentage = 0.80 )
+ convertButtons.active = CheckIfCollisionAndSocketOwnerIsValid()
+ convertButtons.enabled = CheckIfCollisionAndSocketOwnerIsValid()
+ convertButtons = convertButtons.column()
+ convertButtons.operator("object.converttoboxcollision", icon='MESH_CUBE')
+ convertButtons.operator("object.converttoconvexcollision", icon='MESH_ICOSPHERE')
+ convertButtons.operator("object.converttocapsulecollision", icon='MESH_CAPSULE')
+ convertButtons.operator("object.converttospherecollision", icon='SOLID')
+ convertButtons.operator("object.converttosocket", icon='OUTLINER_DATA_EMPTY')
+
+
+class ue4CheckCorrect(bpy.types.Panel): #Is Check and correct panel
+ bl_idname = "panel.ue4.CheckCorrect"
+ bl_label = "Check and correct"
+ bl_space_type = "VIEW_3D"
+ bl_region_type = "TOOLS"
+ bl_category = "Unreal Engine 4"
+
+ def draw(self, context):
+ scn = context.scene
+ props = self.layout.row().operator("object.correctproperty", icon='FILE_TICK')
+
+
+class ue4ExportPanel(bpy.types.Panel): #Is Export panel
+ bl_idname = "panel.ue4.export"
+ bl_label = "Export"
+ bl_space_type = "VIEW_3D"
+ bl_region_type = "TOOLS"
+ bl_category = "Unreal Engine 4"
+
+ def draw(self, context):
+ scn = context.scene
+ self.layout.prop(scn, 'StaticPrefixExportName', icon='OBJECT_DATA')
+ self.layout.prop(scn, 'SkeletalPrefixExportName', icon='OBJECT_DATA')
+ self.layout.prop(scn, 'AnimPrefixExportName', icon='OBJECT_DATA')
+ self.layout.prop(scn, 'ExportNameFilePath')
+ props = self.layout.row().operator("object.exportforunreal", icon='EXPORT')
+
+#### Buttons
+class SelectExportAndChildButton(bpy.types.Operator):
+ bl_label = "Select all \"Export and childs\" objects"
+ bl_idname = "object.selectexport"
+ bl_description = "Select all root objects that will be exported"
+
+ def execute(self, context):
+ for obj in FindAllObjetsByExportType("export_and_childs"):
+ obj.select = True
+ return {'FINISHED'}
+
+
+class DeselectExportAndChildButton(bpy.types.Operator):
+ bl_label = "Deselect all \"Export and childs\" objects"
+ bl_idname = "object.deselectexport"
+ bl_description = "Deselect all root objects that will be exported"
+
+ def execute(self, context):
+ for obj in FindAllObjetsByExportType("export_and_childs"):
+ obj.select = False
+ return {'FINISHED'}
+
+
+class SetOwnerByActive(bpy.types.Operator):
+ bl_label = "Set owner by active selection"
+ bl_idname = "object.setownerbyactive"
+ bl_description = "Set owner by active selection"
+
+ def execute(self, context):
+ try:
+ bpy.context.scene["CollisionAndSocketOwner"] = bpy.context.active_object.name
+ except:
+ pass
+ bpy.context.scene["CollisionAndSocketOwner"] = ""
+ return {'FINISHED'}
+
+
+class ConvertToUECollisionButtonBox(bpy.types.Operator):
+ bl_label = "Convert to box (UBX)"
+ bl_idname = "object.converttoboxcollision"
+ bl_description = "Convert selected mesh(s) to Unreal collision ready for export (Boxes type)"
+
+ def execute(self, context):
+ ConvertToUe4Collision("Box")
+ self.report({'INFO'}, "Selected objet(s) have be converted to UE4 Box collisions")
+ return {'FINISHED'}
+
+
+class ConvertToUECollisionButtonCapsule(bpy.types.Operator):
+ bl_label = "Convert to capsule (UCP)"
+ bl_idname = "object.converttocapsulecollision"
+ bl_description = "Convert selected mesh(s) to Unreal collision ready for export (Capsules type)"
+
+ def execute(self, context):
+ ConvertToUe4Collision("Capsule")
+ self.report({'INFO'}, "Selected objet(s) have be converted to UE4 Capsule collisions")
+ return {'FINISHED'}
+
+
+class ConvertToUECollisionButtonSphere(bpy.types.Operator):
+ bl_label = "Convert to sphere (USP)"
+ bl_idname = "object.converttospherecollision"
+ bl_description = "Convert selected mesh(s) to Unreal collision ready for export (Spheres type)"
+
+ def execute(self, context):
+ ConvertToUe4Collision("Sphere")
+ self.report({'INFO'}, "Selected objet(s) have be converted to UE4 Sphere collisions")
+ return {'FINISHED'}
+
+
+class ConvertToUECollisionButtonConvex(bpy.types.Operator):
+ bl_label = "Convert to convex shape (UCX)"
+ bl_idname = "object.converttoconvexcollision"
+ bl_description = "Convert selected mesh(s) to Unreal collision ready for export (Convex shapes type)"
+
+ def execute(self, context):
+ ConvertToUe4Collision("Convex")
+ self.report({'INFO'}, "Selected objet(s) have be converted to UE4 Convex collisions")
+ return {'FINISHED'}
+
+
+class ConvertToUESocketButton(bpy.types.Operator):
+ bl_label = "Convert to socket (SOCKET)"
+ bl_idname = "object.converttosocket"
+ bl_description = "Convert selected empty(s) to Unreal sockets ready for export"
+
+ def execute(self, context):
+ ConvertToUe4Socket()
+ self.report({'INFO'}, "Selected empty(s) have be converted to UE4 Socket")
+ return {'FINISHED'}
+
+class ExportForUnrealEngineButton(bpy.types.Operator):
+ bl_label = "Export for UnrealEngine 4"
+ bl_idname = "object.exportforunreal"
+ bl_description = "Export all objet intended for export in scene to fbx"
+
+ def execute(self, context):
+ ChecksProp(bpy.context.scene)
+ ExportAllByList(FindAllObjetsByExportType("export_and_childs"))
+ ExportComplete(self)
+ return {'FINISHED'}
+
+
+class CorrectBadPropertyButton(bpy.types.Operator):
+ bl_label = "Correct bad property"
+ bl_idname = "object.correctproperty"
+ bl_description = "Corrects bad properties"
+
+ def execute(self, context):
+ CorrectBadProperty()
+ return {'FINISHED'}
+
+
+#############################[...]#############################
+
+
+def register():
+ bpy.utils.register_module(__name__)
+ bpy.types.Scene.my_prop = bpy.props.StringProperty(default="default value")
+ initObjectProperties()
+ initSceneProperties()
+
+
+def unregister():
+ bpy.utils.unregister_module(__name__)
+
+
+if __name__ == "__main__":
register()
\ No newline at end of file