Newer
Older
for i in scene.objects: i.select = False # deselect all objects
scene.objects.active = mesh
setmesh = mesh
mesh = triangulate_mesh(mesh)
if bpy.types.Scene.udk_copy_merge == True:
bpy.context.scene.objects.unlink(setmesh)
#print("FACES----:",len(mesh.data.tessfaces))
verbose("Working mesh object: {}".format(mesh.name))
CoDEmanX
committed
#collect a list of the material names
print("Materials...")
CoDEmanX
committed
mat_slot_index = 0
for slot in mesh.material_slots:
print(" Material {} '{}'".format(mat_slot_index, slot.name))
MaterialName.append(slot.name)
#if slot.material.texture_slots[0] != None:
#if slot.material.texture_slots[0].texture.image.filepath != None:
CoDEmanX
committed
#print(" Texture path {}".format(slot.material.texture_slots[0].texture.image.filepath))
#create the current material
v_material = psk.GetMatByIndex(mat_slot_index)
v_material.MaterialName = slot.name
v_material.TextureIndex = mat_slot_index
v_material.AuxMaterial = mat_slot_index
mat_slot_index += 1
verbose(" PSK index {}".format(v_material.TextureIndex))
#END slot in mesh.material_slots
CoDEmanX
committed
# object_mat = mesh.materials[0]
#object_material_index = mesh.active_material_index
#FIXME ^ this is redundant due to "= face.material_index" in face loop
wedges = ObjMap()
points_linked = {}
CoDEmanX
committed
discarded_face_count = 0
smoothgroup_list = parse_smooth_groups(mesh.data)
CoDEmanX
committed
print("{} faces".format(len(mesh.data.tessfaces)))
CoDEmanX
committed
print("Smooth groups active:", bpy.context.scene.udk_option_smoothing_groups)
CoDEmanX
committed
for face in mesh.data.tessfaces:
CoDEmanX
committed
smoothgroup_id = 0x80000000
CoDEmanX
committed
for smooth_group in smoothgroup_list:
if smooth_group.contains_face(face):
smoothgroup_id = smooth_group.id
break
#print ' -- Dumping UVs -- '
#print current_face.uv_textures
# modified by VendorX
object_material_index = face.material_index
CoDEmanX
committed
if len(face.vertices) != 3:
raise Error("Non-triangular face (%i)" % len(face.vertices))
CoDEmanX
committed
#RG - apparently blender sometimes has problems when you do quad to triangle
# conversion, and ends up creating faces that have only TWO points -
CoDEmanX
committed
# one of the points is simply in the vertex list for the face twice.
# This is bad, since we can't get a real face normal for a LINE, we need
# a plane for this. So, before we add the face to the list of real faces,
# ensure that the face is actually a plane, and not a line. If it is not
# planar, just discard it and notify the user in the console after we're
# done dumping the rest of the faces
CoDEmanX
committed
if not is_1d_face(face, mesh.data):
CoDEmanX
committed
wedge_list = []
vect_list = []
CoDEmanX
committed
#get or create the current material
psk.GetMatByIndex(object_material_index)
face_index = face.index
has_uv = False
face_uv = None
CoDEmanX
committed
if len(mesh.data.uv_textures) > 0:
CoDEmanX
committed
has_uv = True
uv_layer = mesh.data.tessface_uv_textures.active
face_uv = uv_layer.data[face_index]
#size(data) is number of texture faces. Each face has UVs
#print("DATA face uv: ",len(faceUV.uv), " >> ",(faceUV.uv[0][0]))
CoDEmanX
committed
for i in range(3):
vert_index = face.vertices[i]
vert = mesh.data.vertices[vert_index]
uv = []
#assumes 3 UVs Per face (for now)
if (has_uv):
if len(face_uv.uv) != 3:
print("WARNING: face has more or less than 3 UV coordinates - writing 0,0...")
uv = [0.0, 0.0]
else:
uv = [face_uv.uv[i][0],face_uv.uv[i][1]] #OR bottom works better # 24 for cube
else:
#print ("No UVs?")
uv = [0.0, 0.0]
CoDEmanX
committed
#flip V coordinate because UEd requires it and DOESN'T flip it on its own like it
#does with the mesh Y coordinates. this is otherwise known as MAGIC-2
uv[1] = 1.0 - uv[1]
CoDEmanX
committed
# clamp UV coords if udk_option_clamp_uv is True
if bpy.context.scene.udk_option_clamp_uv:
if (uv[0] > 1):
uv[0] = 1
if (uv[0] < 0):
uv[0] = 0
if (uv[1] > 1):
uv[1] = 1
if (uv[1] < 0):
uv[1] = 0
CoDEmanX
committed
# RE - Append untransformed vector (for normal calc below)
# TODO: convert to Blender.Mathutils
vect_list.append( FVector(vert.co.x, vert.co.y, vert.co.z) )
CoDEmanX
committed
# Transform position for export
#vpos = vert.co * object_material_index
#should fixed this!!
vpos = mesh.matrix_local * vert.co
if bpy.context.scene.udk_option_scale < 0 or bpy.context.scene.udk_option_scale > 1:
vpos.x = vpos.x * bpy.context.scene.udk_option_scale
vpos.y = vpos.y * bpy.context.scene.udk_option_scale
vpos.z = vpos.z * bpy.context.scene.udk_option_scale
#print("scale pos:", vpos)
# Create the point
p = VPoint()
p.Point.X = vpos.x
p.Point.Y = vpos.y
p.Point.Z = vpos.z
if bpy.context.scene.udk_option_smoothing_groups:#is this necessary?
p.SmoothGroup = smoothgroup_id
lPoint = VPointSimple()
lPoint.Point.X = vpos.x
lPoint.Point.Y = vpos.y
lPoint.Point.Z = vpos.z
CoDEmanX
committed
if lPoint in points_linked:
if not(p in points_linked[lPoint]):
points_linked[lPoint].append(p)
else:
points_linked[lPoint] = [p]
CoDEmanX
committed
# Create the wedge
w = VVertex()
w.MatIndex = object_material_index
w.PointIndex = points.get(p) # store keys
w.U = uv[0]
w.V = uv[1]
if bpy.context.scene.udk_option_smoothing_groups:#is this necessary?
w.SmoothGroup = smoothgroup_id
index_wedge = wedges.get(w)
wedge_list.append(index_wedge)
CoDEmanX
committed
#print results
#print("result PointIndex={}, U={:.6f}, V={:.6f}, wedge_index={}".format(
# w.PointIndex,
# w.U,
# w.V,
# index_wedge))
CoDEmanX
committed
#END for i in range(3)
# Determine face vertex order
CoDEmanX
committed
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
# TODO: convert to Blender.Mathutils
# get normal from blender
no = face.normal
# convert to FVector
norm = FVector(no[0], no[1], no[2])
# Calculate the normal of the face in blender order
tnorm = vect_list[1].sub(vect_list[0]).cross(vect_list[2].sub(vect_list[1]))
# RE - dot the normal from blender order against the blender normal
# this gives the product of the two vectors' lengths along the blender normal axis
# all that matters is the sign
dot = norm.dot(tnorm)
tri = VTriangle()
# RE - magic: if the dot product above > 0, order the vertices 2, 1, 0
# if the dot product above < 0, order the vertices 0, 1, 2
# if the dot product is 0, then blender's normal is coplanar with the face
# and we cannot deduce which side of the face is the outside of the mesh
if dot > 0:
(tri.WedgeIndex2, tri.WedgeIndex1, tri.WedgeIndex0) = wedge_list
elif dot < 0:
(tri.WedgeIndex0, tri.WedgeIndex1, tri.WedgeIndex2) = wedge_list
else:
dindex0 = face.vertices[0];
dindex1 = face.vertices[1];
dindex2 = face.vertices[2];
CoDEmanX
committed
mesh.data.vertices[dindex0].select = True
mesh.data.vertices[dindex1].select = True
mesh.data.vertices[dindex2].select = True
CoDEmanX
committed
raise Error("Normal coplanar with face! points:", mesh.data.vertices[dindex0].co, mesh.data.vertices[dindex1].co, mesh.data.vertices[dindex2].co)
CoDEmanX
committed
face.select = True
if face.use_smooth == True:
tri.SmoothingGroups = 1
else:
tri.SmoothingGroups = 0
tri.MatIndex = object_material_index
if bpy.context.scene.udk_option_smoothing_groups:
tri.SmoothingGroups = smoothgroup_id
CoDEmanX
committed
psk.AddFace(tri)
CoDEmanX
committed
#END if not is_1d_face(current_face, mesh.data)
else:
discarded_face_count += 1
CoDEmanX
committed
#END face in mesh.data.faces
CoDEmanX
committed
print("{} points".format(len(points.dict)))
CoDEmanX
committed
for point in points.items():
psk.AddPoint(point)
CoDEmanX
committed
if len(points.dict) > 32767:
raise Error("Mesh vertex limit exceeded! {} > 32767".format(len(points.dict)))
CoDEmanX
committed
print("{} wedges".format(len(wedges.dict)))
CoDEmanX
committed
for wedge in wedges.items():
psk.AddWedge(wedge)
CoDEmanX
committed
# alert the user to degenerate face issues
if discarded_face_count > 0:
print("WARNING: Mesh contained degenerate faces (non-planar)")
print(" Discarded {} faces".format(discarded_face_count))
CoDEmanX
committed
#RG - walk through the vertex groups and find the indexes into the PSK points array
#for them, then store that index and the weight as a tuple in a new list of
#verts for the group that we can look up later by bone name, since Blender matches
#verts to bones for influences by having the VertexGroup named the same thing as
#the bone
CoDEmanX
committed
#[print(x, len(points_linked[x])) for x in points_linked]
#print("pointsindex length ",len(points_linked))
#vertex group
CoDEmanX
committed
# all vertex groups of the mesh (obj)...
for obj_vertex_group in mesh.vertex_groups:
CoDEmanX
committed
#print(" bone group build:",obj_vertex_group.name)#print bone name
#print(dir(obj_vertex_group))
verbose("obj_vertex_group.name={}".format(obj_vertex_group.name))
CoDEmanX
committed
vertex_list = []
CoDEmanX
committed
# all vertices in the mesh...
for vertex in mesh.data.vertices:
#print(dir(vertex))
# all groups this vertex is a member of...
for vgroup in vertex.groups:
if vgroup.group == obj_vertex_group.index:
vertex_weight = vgroup.weight
p = VPointSimple()
vpos = mesh.matrix_local * vertex.co
if bpy.context.scene.udk_option_scale < 0 or bpy.context.scene.udk_option_scale > 1:
vpos.x = vpos.x * bpy.context.scene.udk_option_scale
vpos.y = vpos.y * bpy.context.scene.udk_option_scale
vpos.z = vpos.z * bpy.context.scene.udk_option_scale
p.Point.X = vpos.x
CoDEmanX
committed
p.Point.Y = vpos.y
p.Point.Z = vpos.z
#print(p)
#print(len(points_linked[p]))
try: #check if point doesn't give error
for point in points_linked[p]:
point_index = points.get(point) #point index
v_item = (point_index, vertex_weight)
vertex_list.append(v_item)
except Exception:#if get error ignore them #not safe I think
print("Error link points!")
pass
CoDEmanX
committed
#bone name, [point id and wieght]
#print("Add Vertex Group:",obj_vertex_group.name, " No. Points:",len(vertex_list))
psk.VertexGroups[obj_vertex_group.name] = vertex_list
CoDEmanX
committed
# remove the temporary triangulated mesh
if bpy.context.scene.udk_option_triangulate == True:
verbose("Removing temporary triangle mesh: {}".format(mesh.name))
bpy.ops.object.mode_set(mode='OBJECT') # OBJECT mode
mesh.parent = None # unparent to avoid phantom links
bpy.context.scene.objects.unlink(mesh) # unlink
#===========================================================================
# Collate bones that belong to the UDK skeletal mesh
#===========================================================================
def parse_armature( armature, psk, psa ):
CoDEmanX
committed
print(header("ARMATURE", 'RIGHT'))
verbose("Armature object: {} Armature data: {}".format(armature.name, armature.data.name))
CoDEmanX
committed
# generate a list of root bone candidates
root_candidates = [b for b in armature.data.bones if b.parent == None and b.use_deform == True]
CoDEmanX
committed
# should be a single, unambiguous result
if len(root_candidates) == 0:
raise Error("Cannot find root for UDK bones. The root bone must use deform.")
CoDEmanX
committed
if len(root_candidates) > 1:
raise Error("Ambiguous root for UDK. More than one root bone is using deform.")
CoDEmanX
committed
# prep for bone collection
udk_root_bone = root_candidates[0]
udk_bones = []
BoneUtil.static_bone_id = 0 # replaces global
CoDEmanX
committed
# traverse bone chain
print("{: <3} {: <48} {: <20}".format("ID", "Bone", "Status"))
print()
recurse_bone(udk_root_bone, udk_bones, psk, psa, 0, armature.matrix_local)
CoDEmanX
committed
# final validation
if len(udk_bones) < 3:
raise Error("Less than three bones may crash UDK (legacy issue?)")
CoDEmanX
committed
# return a list of bones making up the entire udk skel
# this is passed to parse_animation instead of working from keyed bones in the action
return udk_bones
#===========================================================================
# bone current bone
# bones bone list
# psk the PSK file object
# psa the PSA file object
# parent_id
# parent_matrix
# indent text indent for recursive log
#===========================================================================
def recurse_bone( bone, bones, psk, psa, parent_id, parent_matrix, indent="" ):
CoDEmanX
committed
status = "Ok"
CoDEmanX
committed
bones.append(bone);
if not bone.use_deform:
status = "No effect"
CoDEmanX
committed
# calc parented bone transform
if bone.parent != None:
quat = make_fquat(bone.matrix.to_quaternion())
quat_parent = bone.parent.matrix.to_quaternion().inverted()
parent_head = quat_parent * bone.parent.head
parent_tail = quat_parent * bone.parent.tail
translation = (parent_tail - parent_head) + bone.head
# calc root bone transform
else:
translation = parent_matrix * bone.head # ARMATURE OBJECT Location
rot_matrix = bone.matrix * parent_matrix.to_3x3() # ARMATURE OBJECT Rotation
quat = make_fquat_default(rot_matrix.to_quaternion())
#udk_option_scale bones here?
if bpy.context.scene.udk_option_scale < 0 or bpy.context.scene.udk_option_scale > 1:
translation.x = translation.x * bpy.context.scene.udk_option_scale
translation.y = translation.y * bpy.context.scene.udk_option_scale
translation.z = translation.z * bpy.context.scene.udk_option_scale
bone_id = BoneUtil.static_bone_id # ALT VERS
BoneUtil.static_bone_id += 1 # ALT VERS
CoDEmanX
committed
child_count = len(bone.children)
CoDEmanX
committed
psk.AddBone( make_vbone(bone.name, parent_id, child_count, quat, translation) )
psa.StoreBone( make_namedbonebinary(bone.name, parent_id, child_count, quat, translation, 1) )
CoDEmanX
committed
#RG - dump influences for this bone - use the data we collected in the mesh dump phase to map our bones to vertex groups
if bone.name in psk.VertexGroups:
vertex_list = psk.VertexGroups[bone.name]
#print("vertex list:", len(vertex_list), " of >" ,bone.name )
for vertex_data in vertex_list:
point_index = vertex_data[0]
vertex_weight = vertex_data[1]
influence = VRawBoneInfluence()
influence.Weight = vertex_weight
influence.BoneIndex = bone_id
influence.PointIndex = point_index
#print (" AddInfluence to vertex {}, weight={},".format(point_index, vertex_weight))
psk.AddInfluence(influence)
else:
status = "No vertex group"
#FIXME overwriting previous status error?
CoDEmanX
committed
print("{:<3} {:<48} {:<20}".format(bone_id, indent+bone.name, status))
CoDEmanX
committed
#bone.matrix_local
#recursively dump child bones
CoDEmanX
committed
for child_bone in bone.children:
recurse_bone(child_bone, bones, psk, psa, bone_id, parent_matrix, " "+indent)
# FIXME rename? remove?
class BoneUtil:
static_bone_id = 0 # static property to replace global
#===========================================================================
# armature the armature
# udk_bones list of bones to be exported
# actions_to_export list of actions to process for export
# psa the PSA file object
#===========================================================================
def parse_animation( armature, udk_bones, actions_to_export, psa ):
CoDEmanX
committed
print(header("ANIMATION", 'RIGHT'))
CoDEmanX
committed
context = bpy.context
anim_rate = context.scene.render.fps
CoDEmanX
committed
verbose("Armature object: {}".format(armature.name))
print("Scene: {} FPS: {} Frames: {} to {}".format(context.scene.name, anim_rate, context.scene.frame_start, context.scene.frame_end))
print("Processing {} action(s)".format(len(actions_to_export)))
print()
if armature.animation_data == None: #if animation data was not create for the armature it will skip the exporting action set(s)
print("None Actions Set! skipping...")
return
restoreAction = armature.animation_data.action # Q: is animation_data always valid?
CoDEmanX
committed
restoreFrame = context.scene.frame_current # we already do this in export_proxy, but we'll do it here too for now
raw_frame_index = 0 # used to set FirstRawFrame, seperating actions in the raw keyframe array
CoDEmanX
committed
# action loop...
for action in actions_to_export:
CoDEmanX
committed
# removed: check for armature with no animation; all it did was force you to add one
if not len(action.fcurves):
print("{} has no keys, skipping".format(action.name))
continue
# apply action to armature and update scene
# note if loop all actions that is not armature it will override and will break armature animation.
armature.animation_data.action = action
context.scene.update()
CoDEmanX
committed
# min/max frames define range
framemin, framemax = action.frame_range
start_frame = int(framemin)
end_frame = int(framemax)
scene_range = range(start_frame, end_frame + 1)
frame_count = len(scene_range)
CoDEmanX
committed
# create the AnimInfoBinary
anim = AnimInfoBinary()
anim.Name = action.name
anim.Group = "" # unused?
anim.NumRawFrames = frame_count
anim.AnimRate = anim_rate
anim.FirstRawFrame = raw_frame_index
CoDEmanX
committed
print("{}, frames {} to {} ({} frames)".format(action.name, start_frame, end_frame, frame_count))
CoDEmanX
committed
# removed: bone lookup table
CoDEmanX
committed
# build a list of pose bones relevant to the collated udk_bones
# fixme: could be done once, prior to loop?
udk_pose_bones = []
for b in udk_bones:
for pb in armature.pose.bones:
if b.name == pb.name:
udk_pose_bones.append(pb)
break;
# sort in the order the bones appear in the PSA file
ordered_bones = {}
ordered_bones = sorted([(psa.UseBone(b.name), b) for b in udk_pose_bones], key=operator.itemgetter(0))
CoDEmanX
committed
# NOTE: posebone.bone references the obj/edit bone
# REMOVED: unique_bone_indexes is redundant?
CoDEmanX
committed
# frame loop...
for i in range(frame_count):
CoDEmanX
committed
frame = scene_range[i]
CoDEmanX
committed
#verbose("FRAME {}".format(i), i) # test loop sampling
CoDEmanX
committed
# advance to frame (automatically updates the pose)
context.scene.frame_set(frame)
CoDEmanX
committed
# compute the key for each bone
for bone_data in ordered_bones:
CoDEmanX
committed
bone_index = bone_data[0]
pose_bone = bone_data[1]
pose_bone_matrix = mathutils.Matrix(pose_bone.matrix)
CoDEmanX
committed
if pose_bone.parent != None:
pose_bone_parent_matrix = mathutils.Matrix(pose_bone.parent.matrix)
pose_bone_matrix = pose_bone_parent_matrix.inverted() * pose_bone_matrix
CoDEmanX
committed
head = pose_bone_matrix.to_translation()
quat = pose_bone_matrix.to_quaternion().normalized()
CoDEmanX
committed
if pose_bone.parent != None:
quat = make_fquat(quat)
else:
quat = make_fquat_default(quat)
CoDEmanX
committed
#scale animation position here?
if bpy.context.scene.udk_option_scale < 0 or bpy.context.scene.udk_option_scale > 1:
head.x = head.x * bpy.context.scene.udk_option_scale
head.y = head.y * bpy.context.scene.udk_option_scale
head.z = head.z * bpy.context.scene.udk_option_scale
vkey = VQuatAnimKey()
vkey.Position.X = head.x
vkey.Position.Y = head.y
vkey.Position.Z = head.z
vkey.Orientation = quat
CoDEmanX
committed
# frame delta = 1.0 / fps
vkey.Time = 1.0 / float(anim_rate) # according to C++ header this is "disregarded"
CoDEmanX
committed
psa.AddRawKey(vkey)
CoDEmanX
committed
# END for bone_data in ordered_bones
raw_frame_index += 1
CoDEmanX
committed
# END for i in range(frame_count)
CoDEmanX
committed
anim.TotalBones = len(ordered_bones) # REMOVED len(unique_bone_indexes)
anim.TrackTime = float(frame_count) # frame_count/anim.AnimRate makes more sense, but this is what actually works in UDK
verbose("anim.TotalBones={}, anim.TrackTime={}".format(anim.TotalBones, anim.TrackTime))
CoDEmanX
committed
psa.AddAnimation(anim)
CoDEmanX
committed
# END for action in actions
# restore
armature.animation_data.action = restoreAction
context.scene.frame_set(restoreFrame)
#===========================================================================
# Collate actions to be exported
# Modify this to filter for one, some or all actions. For now use all.
# RETURNS list of actions
#===========================================================================
def collate_actions():
verbose(header("collate_actions"))
actions_to_export = []
CoDEmanX
committed
for action in bpy.data.actions:
if bpy.context.scene.udk_option_selectanimations: # check if needed to select actions set for exporting it
print("Action Set is selected!")
bready = False
for actionlist in bpy.context.scene.udkas_list: #list the action set from the list
if actionlist.name == action.name and actionlist.bmatch == True and actionlist.bexport == True:
bready = True
print("Added Action Set:",action.name)
break
if bready == False:#don't export it
print("Skipping Action Set:",action.name)
continue
verbose(" + {}".format(action.name)) #action set name
actions_to_export.append(action) #add to the action array
CoDEmanX
committed
return actions_to_export
#===========================================================================
# Locate the target armature and mesh for export
# RETURNS armature, mesh
#===========================================================================
def find_armature_and_mesh():
verbose(header("find_armature_and_mesh", 'LEFT', '<', 60))
CoDEmanX
committed
context = bpy.context
active_object = context.active_object
armature = None
mesh = None
CoDEmanX
committed
# TODO:
# this could be more intuitive
#bpy.ops.object.mode_set(mode='OBJECT')
CoDEmanX
committed
if bpy.context.scene.udk_option_selectobjects: #if checked select object true do list object on export
print("select mode:")
if len(bpy.context.scene.udkArm_list) > 0:
print("Armature Name:",bpy.context.scene.udkArm_list[bpy.context.scene.udkArm_list_idx].name)
for obj in bpy.context.scene.objects:
if obj.name == bpy.context.scene.udkArm_list[bpy.context.scene.udkArm_list_idx].name:
armature = obj
break
else:
raise Error("There is no Armature in the list!")
meshselected = []
#parented_meshes = [obj for obj in armature.children if obj.type == 'MESH']
meshes = [obj for obj in bpy.context.scene.objects if obj.type == 'MESH']
for obj in meshes:
#print(dir(obj))
bexportmesh = False
#print("PARENT MESH:",obj.name)
for udkmeshlist in bpy.context.scene.udkmesh_list:
if obj.name == udkmeshlist.name and udkmeshlist.bexport == True:
bexportmesh = True
break
if bexportmesh == True:
print("Mesh Name:",obj.name," < SELECT TO EXPORT!")
meshselected.append(obj)
print("MESH COUNT:",len(meshselected))
# try the active object
if active_object and active_object.type == 'MESH' and len(meshselected) == 0:
if active_object.parent == armature:
mesh = active_object
else:
raise Error("The selected mesh is not parented to the armature")
CoDEmanX
committed
# otherwise, expect a single mesh parented to the armature (other object types are ignored)
else:
print("Number of meshes:",len(meshes))
print("Number of meshes (selected):",len(meshes))
if len(meshes) == 1:
mesh = meshes[0]
CoDEmanX
committed
if len(meshselected) >= 1:
mesh = sortmesh(meshselected)
else:
raise Error("More than one mesh(s) parented to armature. Select object(s)!")
else:
raise Error("No mesh parented to armature")
else: #if not check for select function from the list work the code here
print("normal mode:")
# try the active object
if active_object and active_object.type == 'ARMATURE':
armature = active_object
bpy.ops.object.mode_set(mode='OBJECT')
# otherwise, try for a single armature in the scene
else:
#bpy.ops.object.mode_set(mode='OBJECT')
all_armatures = [obj for obj in bpy.context.scene.objects if obj.type == 'ARMATURE']
CoDEmanX
committed
if len(all_armatures) == 1:#if armature has one scene just assign it
armature = all_armatures[0]
elif len(all_armatures) > 1:#if there more armature then find the select armature
barmselect = False
for _armobj in all_armatures:
if _armobj.select:
armature = _armobj
barmselect = True
break
if barmselect == False:
raise Error("Please select an armatures in the scene")
else:
raise Error("No armatures in scene")
CoDEmanX
committed
verbose("Found armature: {}".format(armature.name))
CoDEmanX
committed
meshselected = []
parented_meshes = [obj for obj in armature.children if obj.type == 'MESH']
CoDEmanX
committed
if len(armature.children) == 0:
raise Error("The selected Armature has no mesh parented to the Armature Object!")
CoDEmanX
committed
for obj in armature.children:
#print(dir(obj))
if obj.type == 'MESH' and obj.select == True:
meshselected.append(obj)
# try the active object
if active_object and active_object.type == 'MESH' and len(meshselected) == 0:
if active_object.parent == armature:
mesh = active_object
else:
raise Error("The selected mesh is not parented to the armature")
CoDEmanX
committed
# otherwise, expect a single mesh parented to the armature (other object types are ignored)
else:
print("Number of meshes:",len(parented_meshes))
print("Number of meshes (selected):",len(meshselected))
if len(parented_meshes) == 1:
mesh = parented_meshes[0]
CoDEmanX
committed
elif len(parented_meshes) > 1:
if len(meshselected) >= 1:
mesh = sortmesh(meshselected)
else:
raise Error("More than one mesh(s) parented to armature. Select object(s)!")
else:
raise Error("No mesh parented to armature")
CoDEmanX
committed
verbose("Found mesh: {}".format(mesh.name))
if mesh == None or armature == None:
raise Error("Check Mesh and Armature are list!")
#if len(armature.pose.bones) == len(mesh.vertex_groups):
#print("Armature and Mesh Vertex Groups matches Ok!")
#else:
#raise Error("Armature bones:" + str(len(armature.pose.bones)) + " Mesh Vertex Groups:" + str(len(mesh.vertex_groups)) +" doesn't match!")
CoDEmanX
committed
#this will check if object need to be rebuild.
if bpy.context.scene.udk_option_rebuildobjects:
#print("INIT... REBUILDING...")
print("REBUILDING ARMATURE...")
#if deform mesh
armature = rebuildarmature(armature) #rebuild the armature to raw . If there IK constraint it will ignore it.
print("REBUILDING MESH...")
mesh = rebuildmesh(mesh) #rebuild the mesh to raw data format.
return armature, mesh
#===========================================================================
# Returns a list of vertex groups in the mesh. Can be modified to filter
# groups as necessary.
# UNUSED
#===========================================================================
def collate_vertex_groups( mesh ):
verbose("collate_vertex_groups")
groups = []
CoDEmanX
committed
for group in mesh.vertex_groups:
CoDEmanX
committed
groups.append(group)
verbose(" " + group.name)
CoDEmanX
committed
return groups
CoDEmanX
committed
#===========================================================================
# Main
#===========================================================================
def export(filepath):
print(header("Export", 'RIGHT'))
bpy.types.Scene.udk_copy_merge = False #in case fail to export set this to default
t = time.clock()
context = bpy.context
CoDEmanX
committed
print("Blender Version {}.{}.{}".format(bpy.app.version[0], bpy.app.version[1], bpy.app.version[2]))
print("Filepath: {}".format(filepath))
CoDEmanX
committed
verbose("PSK={}, PSA={}".format(context.scene.udk_option_export_psk, context.scene.udk_option_export_psa))
CoDEmanX
committed
# find armature and mesh
# [change this to implement alternative methods; raise Error() if not found]
udk_armature, udk_mesh = find_armature_and_mesh()
CoDEmanX
committed
# check misc conditions
if not (udk_armature.scale.x == udk_armature.scale.y == udk_armature.scale.z == 1):
raise Error("bad armature scale: armature object should have uniform scale of 1 (ALT-S)")
CoDEmanX
committed
if not (udk_mesh.scale.x == udk_mesh.scale.y == udk_mesh.scale.z == 1):
raise Error("bad mesh scale: mesh object should have uniform scale of 1 (ALT-S)")
CoDEmanX
committed
if not (udk_armature.location.x == udk_armature.location.y == udk_armature.location.z == 0):
raise Error("bad armature location: armature should be located at origin (ALT-G)")
CoDEmanX
committed
if not (udk_mesh.location.x == udk_mesh.location.y == udk_mesh.location.z == 0):
raise Error("bad mesh location: mesh should be located at origin (ALT-G)")
CoDEmanX
committed
# prep
psk = PSKFile()
psa = PSAFile()
CoDEmanX
committed
# step 1
parse_mesh(udk_mesh, psk)
CoDEmanX
committed
# step 2
udk_bones = parse_armature(udk_armature, psk, psa)
CoDEmanX
committed
# step 3
if context.scene.udk_option_export_psa == True:
actions = collate_actions()
parse_animation(udk_armature, udk_bones, actions, psa)
CoDEmanX
committed
# write files
print(header("Exporting", 'CENTER'))
CoDEmanX
committed
psk_filename = filepath + '.psk'
psa_filename = filepath + '.psa'
CoDEmanX
committed
if context.scene.udk_option_export_psk == True:
print("Skeletal mesh data...")
psk.PrintOut()
CoDEmanX
committed
file = open(psk_filename, "wb")
file.write(psk.dump())
CoDEmanX
committed
file.close()
print("Exported: " + psk_filename)
print()
CoDEmanX
committed
if context.scene.udk_option_export_psa == True:
print("Animation data...")
if not psa.IsEmpty():
psa.PrintOut()
CoDEmanX
committed
file = open(psa_filename, "wb")
file.write(psa.dump())
CoDEmanX
committed
file.close()
print("Exported: " + psa_filename)
else:
print("No Animation (.psa file) to export")
print()
CoDEmanX
committed
#if objects are rebuild do the unlink
if bpy.context.scene.udk_option_rebuildobjects:
print("Unlinking Objects")
print("Armature Object Name:",udk_armature.name) #display object name
bpy.context.scene.objects.unlink(udk_armature) #remove armature from the scene
print("Mesh Object Name:",udk_mesh.name) #display object name
bpy.context.scene.objects.unlink(udk_mesh) #remove mesh from the scene
print("Export completed in {:.2f} seconds".format((time.clock() - t)))
#===========================================================================
# Operator
#===========================================================================
class Operator_UDKExport( bpy.types.Operator ):
Sebastian Nell
committed
"""Export to UDK"""
bl_idname = "object.udk_export"
bl_label = "Export now"
CoDEmanX
committed
def execute(self, context):
print( "\n"*8 )
CoDEmanX
committed
scene = bpy.context.scene
CoDEmanX
committed
scene.udk_option_export_psk = (scene.udk_option_export == '0' or scene.udk_option_export == '2')
scene.udk_option_export_psa = (scene.udk_option_export == '1' or scene.udk_option_export == '2')
CoDEmanX
committed
filepath = get_dst_path()
CoDEmanX
committed
# cache settings
restore_frame = scene.frame_current
CoDEmanX
committed
message = "Finish Export!"
try:
export(filepath)
except Error as err:
print(err.message)
message = err.message
CoDEmanX
committed
# restore settings
scene.frame_set(restore_frame)
CoDEmanX
committed
self.report({'ERROR'}, message)
CoDEmanX
committed
# restore settings
scene.frame_set(restore_frame)
CoDEmanX
committed
return {'FINISHED'}
#===========================================================================
# Operator
#===========================================================================
class Operator_ToggleConsole( bpy.types.Operator ):
Sebastian Nell
committed
"""Show or hide the console"""
bl_idname = "object.toggle_console"
bl_label = "Toggle console"
CoDEmanX
committed
#def invoke(self, context, event):
# bpy.ops.wm.console_toggle()
# return{'FINISHED'}
def execute(self, context):
bpy.ops.wm.console_toggle()
return {'FINISHED'}
#===========================================================================
# Get filepath for export
#===========================================================================
def get_dst_path():
if bpy.context.scene.udk_option_filename_src == '0':
if bpy.context.active_object:
path = os.path.split(bpy.data.filepath)[0] + "\\" + bpy.context.active_object.name# + ".psk"
else:
#path = os.path.split(bpy.data.filepath)[0] + "\\" + "Unknown";
path = os.path.splitext(bpy.data.filepath)[0]# + ".psk"
else:
path = os.path.splitext(bpy.data.filepath)[0]# + ".psk"
return path
#Added by [MGVS]
bpy.types.Scene.udk_option_filename_src = EnumProperty(
name = "Filename",
description = "Sets the name for the files",
items = [ ('0', "From object", "Name will be taken from object name"),
('1', "From Blend", "Name will be taken from .blend file name") ],
default = '0')
CoDEmanX
committed
bpy.types.Scene.udk_option_export_psk = BoolProperty(
name = "bool export psa",
description = "Boolean for exporting psk format (Skeleton Mesh)",
default = True)
bpy.types.Scene.udk_option_export_psa = BoolProperty(
name = "bool export psa",
description = "Boolean for exporting psa format (Animation Data)",
default = True)
bpy.types.Scene.udk_option_clamp_uv = BoolProperty(
name = "Clamp UV",
description = "True is to limit Clamp UV co-ordinates to [0-1]. False is unrestricted (x,y). ",
default = False)
CoDEmanX
committed
bpy.types.Scene.udk_copy_merge = BoolProperty(
name = "Merge Mesh",
description = "This will copy the mesh(s) and merge the object together and unlink the mesh to be remove while exporting the object.",
default = False)
bpy.types.Scene.udk_option_export = EnumProperty(
name = "Export",
description = "What to export",
items = [ ('0', "Mesh only", "Exports the PSK file for the Skeletal Mesh"),
('1', "Animation only", "Export the PSA file for Action Set(s)(Animations Data)"),
('2', "Mesh & Animation", "Export both PSK and PSA files(Skeletal Mesh/Animation(s) Data)") ],
default = '2')
bpy.types.Scene.udk_option_verbose = BoolProperty(
name = "Verbose",
description = "Verbose console output",
default = False)
bpy.types.Scene.udk_option_smoothing_groups = BoolProperty(
name = "Smooth Groups",
description = "Activate hard edges as smooth groups",
default = True)
bpy.types.Scene.udk_option_triangulate = BoolProperty(
name = "Triangulate Mesh",
description = "Convert Quads to Triangles",
default = False)
CoDEmanX
committed
bpy.types.Scene.udk_option_selectanimations = BoolProperty(
name = "Select Animation(s)",
description = "Select animation(s) for export to psa file.",
default = False)
CoDEmanX
committed
bpy.types.Scene.udk_option_selectobjects = BoolProperty(
name = "Select Object(s)",
description = "Select Armature and Mesh(s). Just make sure mesh(s) is parent to armature.",
default = False)
CoDEmanX
committed
bpy.types.Scene.udk_option_rebuildobjects = BoolProperty(
name = "Rebuild Objects",
description = "In case of deform skeleton mesh and animations data. This will rebuild objects from raw format on export when checked.",
default = False)
CoDEmanX
committed
bpy.types.Scene.udk_option_ignoreactiongroupnames = BoolProperty(
name = "Ignore Action Group Names",
description = "This will Ignore Action Set Group Names Check With Armature Bones. It will override armature to set action set.",
default = False)
bpy.types.Scene.udk_option_scale = FloatProperty(
name = "UDK Scale",
description = "In case you don't want to scale objects manually. This will just scale position when on export for the skeleton mesh and animation data.",
default = 1)
#===========================================================================
# User interface
#===========================================================================
class OBJECT_OT_UTSelectedFaceSmooth(bpy.types.Operator):
Sebastian Nell
committed
"""It will only select smooth faces that is select mesh"""
bl_idname = "object.utselectfacesmooth" # XXX, name???
bl_label = "Select Smooth Faces"#"Select Smooth faces"
CoDEmanX
committed
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
def invoke(self, context, event):
print("----------------------------------------")
print("Init Select Face(s):")
bselected = False
for obj in bpy.data.objects:
if obj.type == 'MESH' and obj.select == True:
smoothcount = 0
flatcount = 0
bpy.ops.object.mode_set(mode='OBJECT')#it need to go into object mode to able to select the faces
for i in bpy.context.scene.objects: i.select = False #deselect all objects
obj.select = True #set current object select
bpy.context.scene.objects.active = obj #set active object
mesh = bmesh.new();
mesh.from_mesh(obj.data)
for face in mesh.faces:
face.select = False
for face in mesh.faces:
if face.smooth == True:
face.select = True
smoothcount += 1
else:
flatcount += 1
face.select = False
mesh.to_mesh(obj.data)
bpy.context.scene.update()
bpy.ops.object.mode_set(mode='EDIT')
print("Select Smooth Count(s):",smoothcount," Flat Count(s):",flatcount)
bselected = True