Newer
Older
#ob_arms[i] = fbxArmObName, ob, arm_my_bones, (ob.action, [])
for bone in my_arm.blenData.bones:
my_bone = my_bone_class(bone, my_arm)
my_arm.fbxBones.append(my_bone)
ob_bones.append(my_bone)
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
# add the meshes to the bones and replace the meshes armature with own armature class
#for obname, ob, mtx, me, mats, arm, armname in ob_meshes:
for my_mesh in ob_meshes:
# Replace
# ...this could be sped up with dictionary mapping but its unlikely for
# it ever to be a bottleneck - (would need 100+ meshes using armatures)
if my_mesh.fbxArm:
for my_arm in ob_arms:
if my_arm.blenObject == my_mesh.fbxArm:
my_mesh.fbxArm = my_arm
break
for my_bone in ob_bones:
# The mesh uses this bones armature!
if my_bone.fbxArm == my_mesh.fbxArm:
if my_bone.blenBone.use_deform:
my_bone.blenMeshes[my_mesh.fbxName] = me
# parent bone: replace bone names with our class instances
# my_mesh.fbxBoneParent is None or a blender bone name initialy, replacing if the names match.
if my_mesh.fbxBoneParent == my_bone.blenName:
my_mesh.fbxBoneParent = my_bone
bone_deformer_count = 0 # count how many bones deform a mesh
my_bone_blenParent = None
for my_bone in ob_bones:
my_bone_blenParent = my_bone.blenBone.parent
if my_bone_blenParent:
for my_bone_parent in ob_bones:
# Note 2.45rc2 you can compare bones normally
if my_bone_blenParent.name == my_bone_parent.blenName and my_bone.fbxArm == my_bone_parent.fbxArm:
my_bone.parent = my_bone_parent
break
# Not used at the moment
# my_bone.calcRestMatrixLocal()
bone_deformer_count += len(my_bone.blenMeshes)
del my_bone_blenParent
# Build blenObject -> fbxObject mapping
# this is needed for groups as well as fbxParenting
bpy.data.objects.tag(False)
# using a list of object names for tagging (Arystan)
tmp_obmapping = {}
for ob_generic in ob_all_typegroups:
for ob_base in ob_generic:
ob_base.blenObject.tag = True
tmp_obmapping[ob_base.blenObject] = ob_base
# Build Groups from objects we export
for blenGroup in bpy.data.groups:
fbxGroupName = None
for ob in blenGroup.objects:
if ob.tag:
if fbxGroupName is None:
fbxGroupName = sane_groupname(blenGroup)
groups.append((fbxGroupName, blenGroup))
tmp_obmapping[ob].fbxGroupNames.append(fbxGroupName) # also adds to the objects fbxGroupNames
groups.sort() # not really needed
# Assign parents using this mapping
for ob_generic in ob_all_typegroups:
for my_ob in ob_generic:
parent = my_ob.blenObject.parent
if parent and parent.tag: # does it exist and is it in the mapping
my_ob.fbxParent = tmp_obmapping[parent]
del tmp_obmapping
# Finished finding groups we use
# == WRITE OBJECTS TO THE FILE ==
# == From now on we are building the FBX file from the information collected above (JCB)
materials = [(sane_matname(mat_tex_pair), mat_tex_pair) for mat_tex_pair in materials.keys()]
textures = [(sane_texname(tex), tex) for tex in textures.keys() if tex]
Campbell Barton
committed
materials.sort(key=lambda m: m[0]) # sort by name
textures.sort(key=lambda m: m[0])
camera_count = 8 if 'CAMERA' in object_types else 0
# sanity checks
try:
assert(not (ob_meshes and ('MESH' not in object_types)))
assert(not (materials and ('MESH' not in object_types)))
assert(not (textures and ('MESH' not in object_types)))
assert(not (textures and ('MESH' not in object_types)))
assert(not (ob_lights and ('LAMP' not in object_types)))
assert(not (ob_cameras and ('CAMERA' not in object_types)))
except AssertionError:
import traceback
traceback.print_exc()
; Object definitions
;------------------------------------------------------------------
Definitions: {
Campbell Barton
committed
Version: 100
Count: %i''' % (
1 + camera_count +
len(ob_meshes) +
len(ob_lights) +
len(ob_cameras) +
len(ob_arms) +
len(ob_null) +
len(ob_bones) +
bone_deformer_count +
len(materials) +
(len(textures) * 2))) # add 1 for global settings
del bone_deformer_count
Campbell Barton
committed
ObjectType: "Model" {
Count: %i
Campbell Barton
committed
camera_count +
len(ob_meshes) +
len(ob_lights) +
len(ob_cameras) +
len(ob_arms) +
len(ob_null) +
Campbell Barton
committed
ObjectType: "Geometry" {
Count: %i
}''' % len(ob_meshes))
if materials:
Campbell Barton
committed
ObjectType: "Material" {
Count: %i
}''' % len(materials))
if textures:
Campbell Barton
committed
ObjectType: "Texture" {
Count: %i
}''' % len(textures)) # add 1 for an empty tex
Campbell Barton
committed
ObjectType: "Video" {
Count: %i
}''' % len(textures)) # add 1 for an empty tex
tmp = 0
# Add deformer nodes
for my_mesh in ob_meshes:
if my_mesh.fbxArm:
tmp += 1
# Add subdeformers
for my_bone in ob_bones:
tmp += len(my_bone.blenMeshes)
if tmp:
Campbell Barton
committed
ObjectType: "Deformer" {
Count: %i
}''' % tmp)
del tmp
# Bind pose is essential for XNA if the 'MESH' is included (JCB)
Campbell Barton
committed
ObjectType: "Pose" {
Count: 1
}''')
if groups:
Campbell Barton
committed
ObjectType: "GroupSelection" {
Count: %i
}''' % len(groups))
Campbell Barton
committed
ObjectType: "GlobalSettings" {
Count: 1
}
}''')
; Object properties
;------------------------------------------------------------------
Objects: {''')
if 'CAMERA' in object_types:
# To comply with other FBX FILES
write_camera_switch()
for my_null in ob_null:
write_null(my_null)
# XNA requires the armature to be a Limb (JCB)
# Note, 2.58 and previous wrote these as normal empties and it worked mostly (except for XNA)
for my_arm in ob_arms:
write_null(my_arm, fbxType="Limb", fbxTypeFlags="Skeleton")
for my_cam in ob_cameras:
write_camera(my_cam)
for my_light in ob_lights:
write_light(my_light)
for my_mesh in ob_meshes:
write_mesh(my_mesh)
#for bonename, bone, obname, me, armob in ob_bones:
for my_bone in ob_bones:
write_bone(my_bone)
if 'CAMERA' in object_types:
write_camera_default()
for matname, (mat, tex) in materials:
write_material(matname, mat) # We only need to have a material per image pair, but no need to write any image info into the material (dumb fbx standard)
# each texture uses a video, odd
for texname, tex in textures:
write_video(texname, tex)
i = 0
for texname, tex in textures:
write_texture(texname, tex, i)
i += 1
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
for groupname, group in groups:
write_group(groupname)
# NOTE - c4d and motionbuilder dont need normalized weights, but deep-exploration 5 does and (max?) do.
# Write armature modifiers
# TODO - add another MODEL? - because of this skin definition.
for my_mesh in ob_meshes:
if my_mesh.fbxArm:
write_deformer_skin(my_mesh.fbxName)
# Get normalized weights for temorary use
if my_mesh.fbxBoneParent:
weights = None
else:
weights = meshNormalizedWeights(my_mesh.blenObject, my_mesh.blenData)
#for bonename, bone, obname, bone_mesh, armob in ob_bones:
for my_bone in ob_bones:
if me in iter(my_bone.blenMeshes.values()):
write_sub_deformer_skin(my_mesh, my_bone, weights)
# Write pose is really weird, only needed when an armature and mesh are used together
# each by themselves do not need pose data. For now only pose meshes and bones
# Bind pose is essential for XNA if the 'MESH' is included (JCB)
Campbell Barton
committed
Pose: "Pose::BIND_POSES", "BindPose" {
Type: "BindPose"
Version: 100
Properties60: {
}
NbPoseNodes: ''')
for fbxName, matrix in pose_items:
fw('\n\t\tPoseNode: {')
fw('\n\t\t\tNode: "Model::%s"' % fbxName)
fw('\n\t\t\tMatrix: %s' % mat4x4str(matrix if matrix else Matrix()))
fw('\n\t\t}')
# Finish Writing Objects
# Write global settings
Campbell Barton
committed
GlobalSettings: {
Version: 1000
Properties60: {
Property: "UpAxis", "int", "",1
Property: "UpAxisSign", "int", "",1
Property: "FrontAxis", "int", "",2
Property: "FrontAxisSign", "int", "",1
Property: "CoordAxis", "int", "",0
Property: "CoordAxisSign", "int", "",1
Property: "UnitScaleFactor", "double", "",1
}
}
''')
; Object relations
;------------------------------------------------------------------
Relations: {''')
# Nulls are likely to cause problems for XNA
for my_null in ob_null:
fw('\n\tModel: "Model::%s", "Null" {\n\t}' % my_null.fbxName)
# Armature must be a Limb for XNA
# Note, 2.58 and previous wrote these as normal empties and it worked mostly (except for XNA)
for my_arm in ob_arms:
fw('\n\tModel: "Model::%s", "Limb" {\n\t}' % my_arm.fbxName)
for my_mesh in ob_meshes:
fw('\n\tModel: "Model::%s", "Mesh" {\n\t}' % my_mesh.fbxName)
# TODO - limbs can have the same name for multiple armatures, should prefix.
#for bonename, bone, obname, me, armob in ob_bones:
for my_bone in ob_bones:
fw('\n\tModel: "Model::%s", "Limb" {\n\t}' % my_bone.fbxName)
for my_cam in ob_cameras:
fw('\n\tModel: "Model::%s", "Camera" {\n\t}' % my_cam.fbxName)
for my_light in ob_lights:
fw('\n\tModel: "Model::%s", "Light" {\n\t}' % my_light.fbxName)
Campbell Barton
committed
Model: "Model::Producer Perspective", "Camera" {
}
Model: "Model::Producer Top", "Camera" {
}
Model: "Model::Producer Bottom", "Camera" {
}
Model: "Model::Producer Front", "Camera" {
}
Model: "Model::Producer Back", "Camera" {
}
Model: "Model::Producer Right", "Camera" {
}
Model: "Model::Producer Left", "Camera" {
}
Model: "Model::Camera Switcher", "CameraSwitcher" {
}''')
for matname, (mat, tex) in materials:
fw('\n\tMaterial: "Material::%s", "" {\n\t}' % matname)
if textures:
for texname, tex in textures:
fw('\n\tTexture: "Texture::%s", "TextureVideoClip" {\n\t}' % texname)
for texname, tex in textures:
fw('\n\tVideo: "Video::%s", "Clip" {\n\t}' % texname)
# deformers - modifiers
for my_mesh in ob_meshes:
if my_mesh.fbxArm:
fw('\n\tDeformer: "Deformer::Skin %s", "Skin" {\n\t}' % my_mesh.fbxName)
#for bonename, bone, obname, me, armob in ob_bones:
for my_bone in ob_bones:
for fbxMeshObName in my_bone.blenMeshes: # .keys() - fbxMeshObName
# is this bone effecting a mesh?
fw('\n\tDeformer: "SubDeformer::Cluster %s %s", "Cluster" {\n\t}' % (fbxMeshObName, my_bone.fbxName))
# This should be at the end
# fw('\n\tPose: "Pose::BIND_POSES", "BindPose" {\n\t}')
for groupname, group in groups:
fw('\n\tGroupSelection: "GroupSelection::%s", "Default" {\n\t}' % groupname)
; Object connections
;------------------------------------------------------------------
Connections: {''')
# NOTE - The FBX SDK does not care about the order but some importers DO!
# for instance, defining the material->mesh connection
# before the mesh->parent crashes cinema4d
for ob_generic in ob_all_typegroups: # all blender 'Object's we support
for my_ob in ob_generic:
# for deformed meshes, don't have any parents or they can get twice transformed.
if my_ob.fbxParent and (not my_ob.fbxArm):
fw('\n\tConnect: "OO", "Model::%s", "Model::%s"' % (my_ob.fbxName, my_ob.fbxParent.fbxName))
else:
fw('\n\tConnect: "OO", "Model::%s", "Model::Scene"' % my_ob.fbxName)
if materials:
for my_mesh in ob_meshes:
# Connect all materials to all objects, not good form but ok for now.
for mat, tex in my_mesh.blenMaterials:
mat_name = mat.name if mat else None
tex_name = tex.name if tex else None
fw('\n\tConnect: "OO", "Material::%s", "Model::%s"' % (sane_name_mapping_mat[mat_name, tex_name], my_mesh.fbxName))
if textures:
for my_mesh in ob_meshes:
if my_mesh.blenTextures:
# fw('\n\tConnect: "OO", "Texture::_empty_", "Model::%s"' % my_mesh.fbxName)
for tex in my_mesh.blenTextures:
if tex:
fw('\n\tConnect: "OO", "Texture::%s", "Model::%s"' % (sane_name_mapping_tex[tex.name], my_mesh.fbxName))
for texname, tex in textures:
fw('\n\tConnect: "OO", "Video::%s", "Texture::%s"' % (texname, texname))
if 'MESH' in object_types:
for my_mesh in ob_meshes:
if my_mesh.fbxArm:
fw('\n\tConnect: "OO", "Deformer::Skin %s", "Model::%s"' % (my_mesh.fbxName, my_mesh.fbxName))
for my_bone in ob_bones:
for fbxMeshObName in my_bone.blenMeshes: # .keys()
fw('\n\tConnect: "OO", "SubDeformer::Cluster %s %s", "Deformer::Skin %s"' % (fbxMeshObName, my_bone.fbxName, fbxMeshObName))
# limbs -> deformers
for my_bone in ob_bones:
for fbxMeshObName in my_bone.blenMeshes: # .keys()
fw('\n\tConnect: "OO", "Model::%s", "SubDeformer::Cluster %s %s"' % (my_bone.fbxName, fbxMeshObName, my_bone.fbxName))
#for bonename, bone, obname, me, armob in ob_bones:
for my_bone in ob_bones:
# Always parent to armature now
if my_bone.parent:
fw('\n\tConnect: "OO", "Model::%s", "Model::%s"' % (my_bone.fbxName, my_bone.parent.fbxName))
else:
# the armature object is written as an empty and all root level bones connect to it
fw('\n\tConnect: "OO", "Model::%s", "Model::%s"' % (my_bone.fbxName, my_bone.fbxArm.fbxName))
# groups
if groups:
for ob_generic in ob_all_typegroups:
for ob_base in ob_generic:
for fbxGroupName in ob_base.fbxGroupNames:
fw('\n\tConnect: "OO", "Model::%s", "GroupSelection::%s"' % (ob_base.fbxName, fbxGroupName))
# I think the following always duplicates the armature connection because it is also in ob_all_typegroups above! (JCB)
# for my_arm in ob_arms:
# fw('\n\tConnect: "OO", "Model::%s", "Model::Scene"' % my_arm.fbxName)
# Needed for scene footer as well as animation
render = scene.render
# from the FBX sdk
#define KTIME_ONE_SECOND KTime (K_LONGLONG(46186158000))
def fbx_time(t):
# 0.5 + val is the same as rounding.
return int(0.5 + ((t / fps) * 46186158000))
fps = float(render.fps)
start = scene.frame_start
end = scene.frame_end
if end < start:
# comment the following line, otherwise we dont get the pose
# if start==end: use_anim = False
# animations for these object types
ob_anim_lists = ob_bones, ob_meshes, ob_null, ob_cameras, ob_lights, ob_arms
if use_anim and [tmp for tmp in ob_anim_lists if tmp]:
frame_orig = scene.frame_current
if use_anim_optimize:
ANIM_OPTIMIZE_PRECISSION_FLOAT = 0.1 ** anim_optimize_precision
# default action, when no actions are avaioable
tmp_actions = []
blenActionDefault = None
action_lastcompat = None
# instead of tagging
tagged_actions = []
# get the current action first so we can use it if we only export one action (JCB)
for my_arm in ob_arms:
if not blenActionDefault:
blenActionDefault = my_arm.blenAction
if blenActionDefault:
break
if use_anim_action_all:
tmp_actions = bpy.data.actions[:]
elif not use_default_take:
if blenActionDefault:
# Export the current action (JCB)
tmp_actions.append(blenActionDefault)
if tmp_actions:
# find which actions are compatible with the armatures
tmp_act_count = 0
for my_arm in ob_arms:
arm_bone_names = set([my_bone.blenName for my_bone in my_arm.fbxBones])
for action in tmp_actions:
action_chan_names = arm_bone_names.intersection(set([g.name for g in action.groups]))
if action_chan_names: # at least one channel matches.
my_arm.blenActionList.append(action)
tagged_actions.append(action.name)
tmp_act_count += 1
# incase there are no actions applied to armatures
# for example, when a user deletes the current action.
action_lastcompat = action
if tmp_act_count:
# unlikely to ever happen but if no actions applied to armatures, just use the last compatible armature.
if not blenActionDefault:
blenActionDefault = action_lastcompat
del action_lastcompat
if use_default_take:
tmp_actions.insert(0, None) # None is the default action
;Takes and animation section
;----------------------------------------------------
Takes: {''')
if blenActionDefault and not use_default_take:
fw('\n\tCurrent: "%s"' % sane_takename(blenActionDefault))
else:
for blenAction in tmp_actions:
# we have tagged all actious that are used be selected armatures
if blenAction:
if blenAction.name in tagged_actions:
print('\taction: "%s" exporting...' % blenAction.name)
else:
print('\taction: "%s" has no armature using it, skipping' % blenAction.name)
continue
if blenAction is None:
# Warning, this only accounts for tmp_actions being [None]
take_name = "Default Take"
act_start = start
act_end = end
else:
# use existing name
take_name = sane_name_mapping_take.get(blenAction.name)
if take_name is None:
take_name = sane_takename(blenAction)
act_start, act_end = blenAction.frame_range
act_start = int(act_start)
act_end = int(act_end)
# Set the action active
for my_arm in ob_arms:
if my_arm.blenObject.animation_data and blenAction in my_arm.blenActionList:
my_arm.blenObject.animation_data.action = blenAction
# Use the action name as the take name and the take filename (JCB)
fw('\n\tTake: "%s" {' % take_name)
fw('\n\t\tFileName: "%s.tak"' % take_name.replace(" ", "_"))
fw('\n\t\tLocalTime: %i,%i' % (fbx_time(act_start - 1), fbx_time(act_end - 1))) # ??? - not sure why this is needed
fw('\n\t\tReferenceTime: %i,%i' % (fbx_time(act_start - 1), fbx_time(act_end - 1))) # ??? - not sure why this is needed
Campbell Barton
committed
;Models animation
;----------------------------------------------------''')
# set pose data for all bones
# do this here incase the action changes
'''
for my_bone in ob_bones:
my_bone.flushAnimData()
'''
i = act_start
while i <= act_end:
scene.frame_set(i)
for ob_generic in ob_anim_lists:
for my_ob in ob_generic:
#Blender.Window.RedrawAll()
if ob_generic == ob_meshes and my_ob.fbxArm:
# We cant animate armature meshes!
my_ob.setPoseFrame(i, fake=True)
else:
my_ob.setPoseFrame(i)
i += 1
#for bonename, bone, obname, me, armob in ob_bones:
for ob_generic in (ob_bones, ob_meshes, ob_null, ob_cameras, ob_lights, ob_arms):
for my_ob in ob_generic:
if ob_generic == ob_meshes and my_ob.fbxArm:
# do nothing,
pass
else:
fw('\n\t\tModel: "Model::%s" {' % my_ob.fbxName) # ??? - not sure why this is needed
fw('\n\t\t\tVersion: 1.1')
fw('\n\t\t\tChannel: "Transform" {')
context_bone_anim_mats = [(my_ob.getAnimParRelMatrix(frame), my_ob.getAnimParRelMatrixRot(frame)) for frame in range(act_start, act_end + 1)]
# ----------------
# ----------------
for TX_LAYER, TX_CHAN in enumerate('TRS'): # transform, rotate, scale
if TX_CHAN == 'T':
context_bone_anim_vecs = [mtx[0].to_translation() for mtx in context_bone_anim_mats]
elif TX_CHAN == 'S':
context_bone_anim_vecs = [mtx[0].to_scale() for mtx in context_bone_anim_mats]
elif TX_CHAN == 'R':
# Was....
# elif TX_CHAN=='R': context_bone_anim_vecs = [mtx[1].to_euler() for mtx in context_bone_anim_mats]
#
# ...but we need to use the previous euler for compatible conversion.
context_bone_anim_vecs = []
prev_eul = None
for mtx in context_bone_anim_mats:
if prev_eul:
prev_eul = mtx[1].to_euler('XYZ', prev_eul)
else:
prev_eul = mtx[1].to_euler()
context_bone_anim_vecs.append(tuple_rad_to_deg(prev_eul))
fw('\n\t\t\t\tChannel: "%s" {' % TX_CHAN) # translation
for i in range(3):
# Loop on each axis of the bone
fw('\n\t\t\t\t\tChannel: "%s" {' % ('XYZ'[i])) # translation
fw('\n\t\t\t\t\t\tDefault: %.15f' % context_bone_anim_vecs[0][i])
fw('\n\t\t\t\t\t\tKeyVer: 4005')
if not use_anim_optimize:
# Just write all frames, simple but in-eficient
fw('\n\t\t\t\t\t\tKeyCount: %i' % (1 + act_end - act_start))
fw('\n\t\t\t\t\t\tKey: ')
frame = act_start
while frame <= act_end:
if frame != act_start:
# Curve types are 'C,n' for constant, 'L' for linear
# C,n is for bezier? - linear is best for now so we can do simple keyframe removal
fw('\n\t\t\t\t\t\t\t%i,%.15f,L' % (fbx_time(frame - 1), context_bone_anim_vecs[frame - act_start][i]))
frame += 1
else:
# remove unneeded keys, j is the frame, needed when some frames are removed.
context_bone_anim_keys = [(vec[i], j) for j, vec in enumerate(context_bone_anim_vecs)]
# last frame to fisrt frame, missing 1 frame on either side.
# removeing in a backwards loop is faster
#for j in xrange( (act_end-act_start)-1, 0, -1 ):
# j = (act_end-act_start)-1
j = len(context_bone_anim_keys) - 2
while j > 0 and len(context_bone_anim_keys) > 2:
# print j, len(context_bone_anim_keys)
# Is this key the same as the ones next to it?
# co-linear horizontal...
if abs(context_bone_anim_keys[j][0] - context_bone_anim_keys[j - 1][0]) < ANIM_OPTIMIZE_PRECISSION_FLOAT and \
abs(context_bone_anim_keys[j][0] - context_bone_anim_keys[j + 1][0]) < ANIM_OPTIMIZE_PRECISSION_FLOAT:
del context_bone_anim_keys[j]
else:
frame_range = float(context_bone_anim_keys[j + 1][1] - context_bone_anim_keys[j - 1][1])
frame_range_fac1 = (context_bone_anim_keys[j + 1][1] - context_bone_anim_keys[j][1]) / frame_range
frame_range_fac2 = 1.0 - frame_range_fac1
if abs(((context_bone_anim_keys[j - 1][0] * frame_range_fac1 + context_bone_anim_keys[j + 1][0] * frame_range_fac2)) - context_bone_anim_keys[j][0]) < ANIM_OPTIMIZE_PRECISSION_FLOAT:
del context_bone_anim_keys[j]
else:
j -= 1
# keep the index below the list length
if j > len(context_bone_anim_keys) - 2:
j = len(context_bone_anim_keys) - 2
if len(context_bone_anim_keys) == 2 and context_bone_anim_keys[0][0] == context_bone_anim_keys[1][0]:
# This axis has no moton, its okay to skip KeyCount and Keys in this case
# pass
# better write one, otherwise we loose poses with no animation
fw('\n\t\t\t\t\t\tKeyCount: 1')
fw('\n\t\t\t\t\t\tKey: ')
fw('\n\t\t\t\t\t\t\t%i,%.15f,L' % (fbx_time(start), context_bone_anim_keys[0][0]))
else:
# We only need to write these if there is at least one
fw('\n\t\t\t\t\t\tKeyCount: %i' % len(context_bone_anim_keys))
fw('\n\t\t\t\t\t\tKey: ')
for val, frame in context_bone_anim_keys:
if frame != context_bone_anim_keys[0][1]: # not the first
# frame is already one less then blenders frame
fw('\n\t\t\t\t\t\t\t%i,%.15f,L' % (fbx_time(frame), val))
if i == 0:
elif i == 1:
elif i == 2:
fw('\n\t\t\t\t\t}')
fw('\n\t\t\t\t\tLayerType: %i' % (TX_LAYER + 1))
fw('\n\t\t\t\t}')
# ---------------
# end the take
# end action loop. set original actions
# do this after every loop incase actions effect eachother.
for my_arm in ob_arms:
if my_arm.blenObject.animation_data:
my_arm.blenObject.animation_data.action = my_arm.blenAction
scene.frame_set(frame_orig)
else:
# no animation
fw('\n;Takes and animation section')
fw('\n;----------------------------------------------------')
fw('\n')
fw('\nTakes: {')
fw('\n\tCurrent: ""')
fw('\n}')
# write meshes animation
#for obname, ob, mtx, me, mats, arm, armname in ob_meshes:
# Clear mesh data Only when writing with modifiers applied
for me in meshes_to_clear:
bpy.data.meshes.remove(me)
# --------------------------- Footer
if world:
m = world.mist_settings
has_mist = m.use_mist
mist_intense = m.intensity
mist_start = m.start
mist_end = m.depth
world_hor = world.horizon_color
else:
world_hor = 0, 0, 0
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
fw('\n;Version 5 settings')
fw('\n;------------------------------------------------------------------')
fw('\n')
fw('\nVersion5: {')
fw('\n\tAmbientRenderSettings: {')
fw('\n\t\tVersion: 101')
fw('\n\t\tAmbientLightColor: %.1f,%.1f,%.1f,0' % tuple(world_amb))
fw('\n\t}')
fw('\n\tFogOptions: {')
fw('\n\t\tFlogEnable: %i' % has_mist)
fw('\n\t\tFogMode: 0')
fw('\n\t\tFogDensity: %.3f' % mist_intense)
fw('\n\t\tFogStart: %.3f' % mist_start)
fw('\n\t\tFogEnd: %.3f' % mist_end)
fw('\n\t\tFogColor: %.1f,%.1f,%.1f,1' % tuple(world_hor))
fw('\n\t}')
fw('\n\tSettings: {')
fw('\n\t\tFrameRate: "%i"' % int(fps))
fw('\n\t\tTimeFormat: 1')
fw('\n\t\tSnapOnFrames: 0')
fw('\n\t\tReferenceTimeIndex: -1')
fw('\n\t\tTimeLineStartTime: %i' % fbx_time(start - 1))
fw('\n\t\tTimeLineStopTime: %i' % fbx_time(end - 1))
fw('\n\t}')
fw('\n\tRendererSetting: {')
fw('\n\t\tDefaultCamera: "Producer Perspective"')
fw('\n\t\tDefaultViewingMode: 0')
fw('\n\t}')
fw('\n}')
fw('\n')
# XXX, shouldnt be global!
for mapping in (sane_name_mapping_ob,
sane_name_mapping_ob_unique,
sane_name_mapping_mat,
sane_name_mapping_tex,
sane_name_mapping_take,
sane_name_mapping_group,
):
mapping.clear()
del mapping
ob_arms[:] = []
ob_bones[:] = []
ob_cameras[:] = []
ob_lights[:] = []
ob_meshes[:] = []
ob_null[:] = []
file.close()
# copy all collected files.
Campbell Barton
committed
bpy_extras.io_utils.path_reference_copy(copy_set)
print('export finished in %.4f sec.' % (time.clock() - start_time))
return {'FINISHED'}
# defaults for applications, currently only unity but could add others.
def defaults_unity3d():
return dict(global_matrix=Matrix.Rotation(-math.pi / 2.0, 4, 'X'),
use_selection=False,
object_types={'ARMATURE', 'EMPTY', 'MESH'},
use_mesh_modifiers=True,
use_anim=True,
use_anim_optimize=False,
use_anim_action_all=True,
batch_mode='OFF',
use_default_take=True,
)
filepath="",
use_selection=False,
batch_mode='OFF',
use_batch_own_dir=False,
**kwargs
):
if bpy.ops.object.mode_set.poll():
bpy.ops.object.mode_set(mode='OBJECT')
if batch_mode == 'OFF':
kwargs_mod = kwargs.copy()
if use_selection:
kwargs_mod["context_objects"] = context.selected_objects
else:
kwargs_mod["context_objects"] = context.scene.objects
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
return save_single(operator, context.scene, filepath, **kwargs_mod)
else:
fbxpath = filepath
prefix = os.path.basename(fbxpath)
if prefix:
fbxpath = os.path.dirname(fbxpath)
if not fbxpath.endswith(os.sep):
fbxpath += os.sep
if batch_mode == 'GROUP':
data_seq = bpy.data.groups
else:
data_seq = bpy.data.scenes
# call this function within a loop with BATCH_ENABLE == False
# no scene switching done at the moment.
# orig_sce = context.scene
new_fbxpath = fbxpath # own dir option modifies, we need to keep an original
for data in data_seq: # scene or group
newname = prefix + bpy.path.clean_name(data.name)
if use_batch_own_dir:
new_fbxpath = fbxpath + newname + os.sep
# path may already exist
# TODO - might exist but be a file. unlikely but should probably account for it.
if not os.path.exists(new_fbxpath):
os.makedirs(new_fbxpath)
filepath = new_fbxpath + newname + '.fbx'
print('\nBatch exporting %s as...\n\t%r' % (data, filepath))
# XXX don't know what to do with this, probably do the same? (Arystan)
if batch_mode == 'GROUP': # group
# group, so objects update properly, add a dummy scene.
scene = bpy.data.scenes.new(name="FBX_Temp")
scene.layers = [True] * 20
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
for ob_base in data.objects:
scene.objects.link(ob_base)
scene.update()
else:
scene = data
# TODO - BUMMER! Armatures not in the group wont animate the mesh
# else: # scene
# data_seq.active = data
# Call self with modified args
# Dont pass batch options since we already usedt them
kwargs_batch = kwargs.copy()
kwargs_batch["context_objects"] = data.objects
save_single(operator, scene, filepath, **kwargs_batch)
if batch_mode == 'GROUP':
# remove temp group scene
bpy.data.scenes.remove(scene)
# no active scene changing!
# bpy.data.scenes.active = orig_sce
return {'FINISHED'} # so the script wont run after we have batch exported.
# APPLICATION REQUIREMENTS
# Please update the lists for UDK, Unity, XNA etc. on the following web page:
# http://wiki.blender.org/index.php/Dev:2.5/Py/Scripts/Import-Export/UnifiedFBX
# XNA FBX Requirements (JCB 29 July 2011)
# - Armature must be parented to the scene
# - Armature must be a 'Limb' never a 'null'. This is in several places.
# - First bone must be parented to the armature.
# - Rotation must be completely disabled including
# always returning the original matrix in In object_tx().
# It is the animation that gets distorted during rotation!
# - Lone edges cause intermittent errors in the XNA content pipeline!
# I have added a warning message and excluded them.
# - Bind pose must be included with the 'MESH'
# Typical settings for XNA export
# No Cameras, No Lamps, No Edges, No face smoothing, No Default_Take, Armature as bone, Disable rotation
# NOTE TO Campbell -
# Can any or all of the following notes be removed because some have been here for a long time? (JCB 27 July 2011)
# NOTES (all line numbers correspond to original export_fbx.py (under release/scripts)
# - get rid of bpy.path.clean_name somehow
# + get rid of BPyObject_getObjectArmature, move it in RNA?
# - implement all BPyMesh_* used here with RNA
# - getDerivedObjects is not fully replicated with .dupli* funcs
# - don't know what those colbits are, do we need them? they're said to be deprecated in DNA_object_types.h: 1886-1893
# - no hq normals: 1900-1901