[Bf-python] [Bf-committers] Blender Need an evaluation

Sybren A. Stüvel sybren at stuvel.eu
Sat Sep 9 17:57:06 CEST 2017


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 at blender.org
> https://lists.blender.org/mailman/listinfo/bf-committers


-- 
Sybren A. Stüvel

https://cloud.blender.org/
https://stuvelfoto.nl/
https://stuvel.eu/
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 181 bytes
Desc: not available
URL: <http://lists.blender.org/pipermail/bf-python/attachments/20170909/651e9354/attachment.sig>


More information about the Bf-python mailing list