Hi Loux,

I haven't looked into the code itself yet, but one thing that
immediately stands out is that it doesn't follow PEP-8 at all. You may
want to look into that, as it'll make life easier for people who
actually want to review.

Cheers,
Sybren

On Sat, Sep 09, 2017 at 03:36:58PM +0200, xavier loux wrote:
> 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
> Bf-committers@blender.org
> https://lists.blender.org/mailman/listinfo/bf-committers


-- 
Sybren A. Stüvel

https://cloud.blender.org/
https://stuvelfoto.nl/
https://stuvel.eu/

Attachment: signature.asc
Description: PGP signature

_______________________________________________
Bf-committers mailing list
Bf-committers@blender.org
https://lists.blender.org/mailman/listinfo/bf-committers

Reply via email to