Hello, I have created an add-ons to easily export content from Blender to
Unreal Engine 4 and I need it to be evaluated and tested to submit it in
Blender. I have attached the .py file (version 0, 1, 1)

Thank you for your feedback.
With best regards, Loux Xavier

Add-ons wiki page :
https://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Import-Export/Blender_For_UnrealEngine
Submission page : https://developer.blender.org/T52622
#====================== 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 <http://www.gnu.org/licenses/>.
#  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, 1),
        'blender': (2, 78, 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':
                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_end = keyframes[-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
        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 = 
[curve.data_path.split('"')[1] for curve in Action.fcurves]

                                        if ChecksRelationship(objBonesName, 
animBonesName):
                                                print("Il ya une correlation")
                                                animExportDir = os.path.join( 
exportDir, "Anim" )
                                                ExportSingleAnimation(obj, 
Action, animExportDir, animPrefix+obj.name+"_"+Action.name+".fbx")
                                        else:
                                                print("Il n'y a pas de 
correlation")
                        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(self)
                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()
_______________________________________________
Bf-committers mailing list
[email protected]
https://lists.blender.org/mailman/listinfo/bf-committers

Reply via email to