Newer
Older
# ##### 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 2
# 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, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
# Script copyright (C) Campbell Barton
import os
import time
import math # math.pi
import bpy
from mathutils import Vector, Matrix
# I guess FBX uses degrees instead of radians (Arystan).
# Call this function just before writing to FBX.
# 180 / math.pi == 57.295779513
def tuple_rad_to_deg(eul):
return eul[0] * 57.295779513, eul[1] * 57.295779513, eul[2] * 57.295779513
# Used to add the scene name into the filepath without using odd chars
sane_name_mapping_ob = {}
sane_name_mapping_ob_unique = set()
sane_name_mapping_mat = {}
sane_name_mapping_tex = {}
sane_name_mapping_take = {}
sane_name_mapping_group = {}
# Make sure reserved names are not used
sane_name_mapping_ob['Scene'] = 'Scene_'
sane_name_mapping_ob_unique.add('Scene_')
def increment_string(t):
name = t
num = ''
while name and name[-1].isdigit():
num = name[-1] + num
name = name[:-1]
if num:
return '%s%d' % (name, int(num) + 1)
else:
return name + '_0'
# todo - Disallow the name 'Scene' - it will bugger things up.
def sane_name(data, dct, unique_set=None):
#if not data: return None
if type(data) == tuple: # materials are paired up with images
data, other = data
use_other = True
else:
other = None
use_other = False
name = data.name if data else None
orig_name = name
if other:
orig_name_other = other.name
name = '%s #%s' % (name, orig_name_other)
else:
orig_name_other = None
# dont cache, only ever call once for each data type now,
# so as to avoid namespace collision between types - like with objects <-> bones
#try: return dct[name]
#except: pass
if not name:
name = 'unnamed' # blank string, ASKING FOR TROUBLE!
else:
name = bpy.path.clean_name(name) # use our own
name_unique = dct.values() if unique_set is None else unique_set
while name in name_unique:
name = increment_string(name)
if use_other: # even if other is None - orig_name_other will be a string or None
dct[orig_name, orig_name_other] = name
else:
dct[orig_name] = name
if unique_set is not None:
unique_set.add(name)
return name
def sane_obname(data):
return sane_name(data, sane_name_mapping_ob, sane_name_mapping_ob_unique)
def sane_matname(data):
return sane_name(data, sane_name_mapping_mat)
def sane_texname(data):
return sane_name(data, sane_name_mapping_tex)
def sane_takename(data):
return sane_name(data, sane_name_mapping_take)
def sane_groupname(data):
return sane_name(data, sane_name_mapping_group)
def mat4x4str(mat):
# blender matrix is row major, fbx is col major so transpose on write
return ("%.15f,%.15f,%.15f,%.15f,"
"%.15f,%.15f,%.15f,%.15f,"
"%.15f,%.15f,%.15f,%.15f,"
"%.15f,%.15f,%.15f,%.15f" %
tuple([f for v in mat.transposed() for f in v]))
def action_bone_names(obj, action):
from bpy.types import PoseBone
names = set()
path_resolve = obj.path_resolve
for fcu in action.fcurves:
try:
prop = path_resolve(fcu.data_path, False)
except:
prop = None
if prop is not None:
data = prop.data
if isinstance(data, PoseBone):
names.add(data.name)
return names
# ob must be OB_MESH
def BPyMesh_meshWeight2List(ob, me):
""" Takes a mesh and return its group names and a list of lists, one list per vertex.
aligning the each vert list with the group names, each list contains float value for the weight.
These 2 lists can be modified and then used with list2MeshWeight to apply the changes.
# Clear the vert group.
groupNames = [g.name for g in ob.vertex_groups]
len_groupNames = len(groupNames)
if not len_groupNames:
# no verts? return a vert aligned empty list
return [[] for i in range(len(me.vertices))], []
else:
vWeightList = [[0.0] * len_groupNames for i in range(len(me.vertices))]
for i, v in enumerate(me.vertices):
for g in v.groups:
Campbell Barton
committed
# possible weights are out of range
index = g.group
if index < len_groupNames:
vWeightList[i][index] = g.weight
return groupNames, vWeightList
def meshNormalizedWeights(ob, me):
Campbell Barton
committed
groupNames, vWeightList = BPyMesh_meshWeight2List(ob, me)
if not groupNames:
return [], []
for i, vWeights in enumerate(vWeightList):
tot = 0.0
for w in vWeights:
tot += w
if tot:
for j, w in enumerate(vWeights):
vWeights[j] = w / tot
return groupNames, vWeightList
header_comment = \
'''; FBX 6.1.0 project file
; Created by Blender FBX Exporter
; for support mail: ideasman42@gmail.com
; ----------------------------------------------------
'''
# This func can be called with just the filepath
def save_single(operator, scene, filepath="",
object_types={'EMPTY', 'CAMERA', 'LAMP', 'ARMATURE', 'MESH'},
use_mesh_modifiers=True,
Campbell Barton
committed
mesh_smooth_type='FACE',
use_armature_deform_only=False,
use_anim=True,
use_anim_optimize=True,
use_anim_action_all=False,
Campbell Barton
committed
use_metadata=True,
use_mesh_edges=True,
use_default_take=True,
):
Campbell Barton
committed
import bpy_extras.io_utils
# Only used for camera and lamp rotations
mtx_x90 = Matrix.Rotation(math.pi / 2.0, 3, 'X')
# Used for mesh and armature rotations
mtx4_z90 = Matrix.Rotation(math.pi / 2.0, 4, 'Z')
if global_matrix is None:
global_matrix = Matrix()
global_scale = 1.0
else:
global_scale = global_matrix.median_scale
# Use this for working out paths relative to the export location
base_src = os.path.dirname(bpy.data.filepath)
Campbell Barton
committed
base_dst = os.path.dirname(filepath)
# collect images to copy
copy_set = set()
# ----------------------------------------------
# storage classes
class my_bone_class(object):
__slots__ = ("blenName",
"blenBone",
"blenMeshes",
"restMatrix",
"parent",
"blenName",
"fbxName",
"fbxArm",
"__pose_bone",
"__anim_poselist")
def __init__(self, blenBone, fbxArm):
# This is so 2 armatures dont have naming conflicts since FBX bones use object namespace
self.fbxName = sane_obname(blenBone)
self.blenName = blenBone.name
self.blenBone = blenBone
self.blenMeshes = {} # fbxMeshObName : mesh
self.fbxArm = fbxArm
self.restMatrix = blenBone.matrix_local
# not used yet
#~ self.restMatrixInv = self.restMatrix.inverted()
#~ self.restMatrixLocal = None # set later, need parent matrix
self.parent = None
# not public
pose = fbxArm.blenObject.pose
self.__pose_bone = pose.bones[self.blenName]
# store a list if matrices here, (poseMatrix, head, tail)
# {frame:posematrix, frame:posematrix, ...}
self.__anim_poselist = {}
'''
def calcRestMatrixLocal(self):
if self.parent:
self.restMatrixLocal = self.restMatrix * self.parent.restMatrix.inverted()
else:
self.restMatrixLocal = self.restMatrix.copy()
'''
def setPoseFrame(self, f):
# cache pose info here, frame must be set beforehand
# Didnt end up needing head or tail, if we do - here it is.
'''
self.__anim_poselist[f] = (\
self.__pose_bone.poseMatrix.copy(),\
self.__pose_bone.head.copy(),\
self.__pose_bone.tail.copy() )
'''
self.__anim_poselist[f] = self.__pose_bone.matrix.copy()
Campbell Barton
committed
def getPoseBone(self):
return self.__pose_bone
# get pose from frame.
def getPoseMatrix(self, f): # ----------------------------------------------
return self.__anim_poselist[f]
'''
def getPoseHead(self, f):
#return self.__pose_bone.head.copy()
return self.__anim_poselist[f][1].copy()
def getPoseTail(self, f):
#return self.__pose_bone.tail.copy()
return self.__anim_poselist[f][2].copy()
'''
# end
def getAnimParRelMatrix(self, frame):
#arm_mat = self.fbxArm.matrixWorld
#arm_mat = self.fbxArm.parRelMatrix()
if not self.parent:
#return mtx4_z90 * (self.getPoseMatrix(frame) * arm_mat) # dont apply arm matrix anymore
return self.getPoseMatrix(frame) * mtx4_z90
else:
#return (mtx4_z90 * ((self.getPoseMatrix(frame) * arm_mat))) * (mtx4_z90 * (self.parent.getPoseMatrix(frame) * arm_mat)).inverted()
return (self.parent.getPoseMatrix(frame) * mtx4_z90).inverted() * ((self.getPoseMatrix(frame)) * mtx4_z90)
# we need thes because cameras and lights modified rotations
def getAnimParRelMatrixRot(self, frame):
return self.getAnimParRelMatrix(frame)
def flushAnimData(self):
self.__anim_poselist.clear()
class my_object_generic(object):
__slots__ = ("fbxName",
"blenObject",
"blenData",
"origData",
"blenTextures",
"blenMaterials",
"blenMaterialList",
"blenAction",
"blenActionList",
"fbxGroupNames",
"fbxParent",
"fbxBoneParent",
"fbxBones",
"fbxArm",
"matrixWorld",
"__anim_poselist",
)
# Other settings can be applied for each type - mesh, armature etc.
def __init__(self, ob, matrixWorld=None):
self.fbxName = sane_obname(ob)
self.blenObject = ob
self.fbxGroupNames = []
self.fbxParent = None # set later on IF the parent is in the selection.
if matrixWorld:
self.matrixWorld = global_matrix * matrixWorld
else:
self.matrixWorld = global_matrix * ob.matrix_world
self.__anim_poselist = {} # we should only access this
def parRelMatrix(self):
if self.fbxParent:
return self.fbxParent.matrixWorld.inverted() * self.matrixWorld
else:
return self.matrixWorld
def setPoseFrame(self, f, fake=False):
if fake:
Campbell Barton
committed
self.__anim_poselist[f] = self.matrixWorld * global_matrix.inverted()
else:
self.__anim_poselist[f] = self.blenObject.matrix_world.copy()
def getAnimParRelMatrix(self, frame):
if self.fbxParent:
#return (self.__anim_poselist[frame] * self.fbxParent.__anim_poselist[frame].inverted() ) * global_matrix
return (global_matrix * self.fbxParent.__anim_poselist[frame]).inverted() * (global_matrix * self.__anim_poselist[frame])
else:
return global_matrix * self.__anim_poselist[frame]
def getAnimParRelMatrixRot(self, frame):
obj_type = self.blenObject.type
if self.fbxParent:
matrix_rot = ((global_matrix * self.fbxParent.__anim_poselist[frame]).inverted() * (global_matrix * self.__anim_poselist[frame])).to_3x3()
else:
matrix_rot = (global_matrix * self.__anim_poselist[frame]).to_3x3()
# Lamps need to be rotated
if obj_type == 'LAMP':
matrix_rot = matrix_rot * mtx_x90
elif obj_type == 'CAMERA':
y = matrix_rot * Vector((0.0, 1.0, 0.0))
matrix_rot = Matrix.Rotation(math.pi / 2.0, 3, y) * matrix_rot
return matrix_rot
# ----------------------------------------------
print('\nFBX export starting... %r' % filepath)
start_time = time.clock()
try:
Campbell Barton
committed
file = open(filepath, "w", encoding="utf8", newline="\n")
except:
import traceback
traceback.print_exc()
operator.report({'ERROR'}, "Couldn't open file %r" % filepath)
# scene = context.scene # now passed as an arg instead of context
world = scene.world
# ---------------------------- Write the header first
Campbell Barton
committed
if use_metadata:
curtime = time.localtime()[0:6]
else:
curtime = (0, 0, 0, 0, 0, 0)
'''FBXHeaderExtension: {
Campbell Barton
committed
FBXHeaderVersion: 1003
FBXVersion: 6100
CreationTimeStamp: {
Version: 1000
Year: %.4i
Month: %.2i
Day: %.2i
Hour: %.2i
Minute: %.2i
Second: %.2i
Millisecond: 0
}
Creator: "FBX SDK/FBX Plugins build 20070228"
OtherFlags: {
FlagPLE: 0
}
}''' % (curtime))
fw('\nCreationTime: "%.4i-%.2i-%.2i %.2i:%.2i:%.2i:000"' % curtime)
fw('\nCreator: "Blender version %s"' % bpy.app.version_string)
pose_items = [] # list of (fbxName, matrix) to write pose data for, easier to collect along the way
# --------------- funcs for exporting
def object_tx(ob, loc, matrix, matrix_mod=None):
Matrix mod is so armature objects can modify their bone matrices
if isinstance(ob, bpy.types.Bone):
# we know we have a matrix
# matrix = mtx4_z90 * (ob.matrix['ARMATURESPACE'] * matrix_mod)
matrix = ob.matrix_local * mtx4_z90 # dont apply armature matrix anymore
parent = ob.parent
if parent:
#par_matrix = mtx4_z90 * (parent.matrix['ARMATURESPACE'] * matrix_mod)
par_matrix = parent.matrix_local * mtx4_z90 # dont apply armature matrix anymore
matrix = par_matrix.inverted() * matrix
loc, rot, scale = matrix.decompose()
matrix_rot = rot.to_matrix()
loc = tuple(loc)
rot = tuple(rot.to_euler()) # quat -> euler
scale = tuple(scale)
else:
# This is bad because we need the parent relative matrix from the fbx parent (if we have one), dont use anymore
#if ob and not matrix: matrix = ob.matrix_world * global_matrix
if ob and not matrix:
raise Exception("error: this should never happen!")
matrix_rot = matrix
#if matrix:
Campbell Barton
committed
# matrix = matrix_scale * matrix
if matrix:
loc, rot, scale = matrix.decompose()
matrix_rot = rot.to_matrix()
# Lamps need to be rotated
if ob and ob.type == 'LAMP':
matrix_rot = matrix_rot * mtx_x90
elif ob and ob.type == 'CAMERA':
y = matrix_rot * Vector((0.0, 1.0, 0.0))
matrix_rot = Matrix.Rotation(math.pi / 2.0, 3, y) * matrix_rot
# else do nothing.
loc = tuple(loc)
rot = tuple(matrix_rot.to_euler())
scale = tuple(scale)
else:
if not loc:
loc = 0.0, 0.0, 0.0
scale = 1.0, 1.0, 1.0
rot = 0.0, 0.0, 0.0
return loc, rot, scale, matrix, matrix_rot
def write_object_tx(ob, loc, matrix, matrix_mod=None):
We have loc to set the location if non blender objects that have a location
matrix_mod is only used for bones at the moment
loc, rot, scale, matrix, matrix_rot = object_tx(ob, loc, matrix, matrix_mod)
fw('\n\t\t\tProperty: "Lcl Translation", "Lcl Translation", "A+",%.15f,%.15f,%.15f' % loc)
fw('\n\t\t\tProperty: "Lcl Rotation", "Lcl Rotation", "A+",%.15f,%.15f,%.15f' % tuple_rad_to_deg(rot))
fw('\n\t\t\tProperty: "Lcl Scaling", "Lcl Scaling", "A+",%.15f,%.15f,%.15f' % scale)
return loc, rot, scale, matrix, matrix_rot
Campbell Barton
committed
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
def get_constraints(ob=None):
# Set variables to their defaults.
constraint_values = {"loc_min": (0.0, 0.0, 0.0),
"loc_max": (0.0, 0.0, 0.0),
"loc_limit": (0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
"rot_min": (0.0, 0.0, 0.0),
"rot_max": (0.0, 0.0, 0.0),
"rot_limit": (0.0, 0.0, 0.0),
"sca_min": (1.0, 1.0, 1.0),
"sca_max": (1.0, 1.0, 1.0),
"sca_limit": (0.0, 0.0, 0.0, 0.0, 0.0, 0.0),
}
# Iterate through the list of constraints for this object to get the information in a format which is compatible with the FBX format.
if ob is not None:
for constraint in ob.constraints:
if constraint.type == 'LIMIT_LOCATION':
constraint_values["loc_min"] = constraint.min_x, constraint.min_y, constraint.min_z
constraint_values["loc_max"] = constraint.max_x, constraint.max_y, constraint.max_z
constraint_values["loc_limit"] = constraint.use_min_x, constraint.use_min_y, constraint.use_min_z, constraint.use_max_x, constraint.use_max_y, constraint.use_max_z
elif constraint.type == 'LIMIT_ROTATION':
constraint_values["rot_min"] = math.degrees(constraint.min_x), math.degrees(constraint.min_y), math.degrees(constraint.min_z)
constraint_values["rot_max"] = math.degrees(constraint.max_x), math.degrees(constraint.max_y), math.degrees(constraint.max_z)
constraint_values["rot_limit"] = constraint.use_limit_x, constraint.use_limit_y, constraint.use_limit_z
elif constraint.type == 'LIMIT_SCALE':
constraint_values["sca_min"] = constraint.min_x, constraint.min_y, constraint.min_z
constraint_values["sca_max"] = constraint.max_x, constraint.max_y, constraint.max_z
constraint_values["sca_limit"] = constraint.use_min_x, constraint.use_min_y, constraint.use_min_z, constraint.use_max_x, constraint.use_max_y, constraint.use_max_z
Campbell Barton
committed
assert(len(constraint_values) == 9)
return constraint_values
def write_object_props(ob=None, loc=None, matrix=None, matrix_mod=None, pose_bone=None):
# Check if a pose exists for this object and set the constraint soruce accordingly. (Poses only exsit if the object is a bone.)
if pose_bone:
constraints = get_constraints(pose_bone)
else:
constraints = get_constraints(ob)
# if the type is 0 its an empty otherwise its a mesh
# only difference at the moment is one has a color
Campbell Barton
committed
Properties60: {
Property: "QuaternionInterpolate", "bool", "",0
Property: "Visibility", "Visibility", "A+",1''')
loc, rot, scale, matrix, matrix_rot = write_object_tx(ob, loc, matrix, matrix_mod)
# Rotation order, note, for FBX files Iv loaded normal order is 1
# setting to zero.
# eEULER_XYZ = 0
# eEULER_XZY
# eEULER_YZX
# eEULER_YXZ
# eEULER_ZXY
# eEULER_ZYX
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
fw('\n\t\t\tProperty: "RotationOffset", "Vector3D", "",0,0,0'
'\n\t\t\tProperty: "RotationPivot", "Vector3D", "",0,0,0'
'\n\t\t\tProperty: "ScalingOffset", "Vector3D", "",0,0,0'
'\n\t\t\tProperty: "ScalingPivot", "Vector3D", "",0,0,0'
'\n\t\t\tProperty: "TranslationActive", "bool", "",0'
)
fw('\n\t\t\tProperty: "TranslationMin", "Vector3D", "",%.15g,%.15g,%.15g' % constraints["loc_min"])
fw('\n\t\t\tProperty: "TranslationMax", "Vector3D", "",%.15g,%.15g,%.15g' % constraints["loc_max"])
fw('\n\t\t\tProperty: "TranslationMinX", "bool", "",%d' % constraints["loc_limit"][0])
fw('\n\t\t\tProperty: "TranslationMinY", "bool", "",%d' % constraints["loc_limit"][1])
fw('\n\t\t\tProperty: "TranslationMinZ", "bool", "",%d' % constraints["loc_limit"][2])
fw('\n\t\t\tProperty: "TranslationMaxX", "bool", "",%d' % constraints["loc_limit"][3])
fw('\n\t\t\tProperty: "TranslationMaxY", "bool", "",%d' % constraints["loc_limit"][4])
fw('\n\t\t\tProperty: "TranslationMaxZ", "bool", "",%d' % constraints["loc_limit"][5])
fw('\n\t\t\tProperty: "RotationOrder", "enum", "",0'
'\n\t\t\tProperty: "RotationSpaceForLimitOnly", "bool", "",0'
'\n\t\t\tProperty: "AxisLen", "double", "",10'
'\n\t\t\tProperty: "PreRotation", "Vector3D", "",0,0,0'
'\n\t\t\tProperty: "PostRotation", "Vector3D", "",0,0,0'
'\n\t\t\tProperty: "RotationActive", "bool", "",0'
)
fw('\n\t\t\tProperty: "RotationMin", "Vector3D", "",%.15g,%.15g,%.15g' % constraints["rot_min"])
fw('\n\t\t\tProperty: "RotationMax", "Vector3D", "",%.15g,%.15g,%.15g' % constraints["rot_max"])
fw('\n\t\t\tProperty: "RotationMinX", "bool", "",%d' % constraints["rot_limit"][0])
fw('\n\t\t\tProperty: "RotationMinY", "bool", "",%d' % constraints["rot_limit"][1])
fw('\n\t\t\tProperty: "RotationMinZ", "bool", "",%d' % constraints["rot_limit"][2])
fw('\n\t\t\tProperty: "RotationMaxX", "bool", "",%d' % constraints["rot_limit"][0])
fw('\n\t\t\tProperty: "RotationMaxY", "bool", "",%d' % constraints["rot_limit"][1])
fw('\n\t\t\tProperty: "RotationMaxZ", "bool", "",%d' % constraints["rot_limit"][2])
fw('\n\t\t\tProperty: "RotationStiffnessX", "double", "",0'
'\n\t\t\tProperty: "RotationStiffnessY", "double", "",0'
'\n\t\t\tProperty: "RotationStiffnessZ", "double", "",0'
'\n\t\t\tProperty: "MinDampRangeX", "double", "",0'
'\n\t\t\tProperty: "MinDampRangeY", "double", "",0'
'\n\t\t\tProperty: "MinDampRangeZ", "double", "",0'
'\n\t\t\tProperty: "MaxDampRangeX", "double", "",0'
'\n\t\t\tProperty: "MaxDampRangeY", "double", "",0'
'\n\t\t\tProperty: "MaxDampRangeZ", "double", "",0'
'\n\t\t\tProperty: "MinDampStrengthX", "double", "",0'
'\n\t\t\tProperty: "MinDampStrengthY", "double", "",0'
'\n\t\t\tProperty: "MinDampStrengthZ", "double", "",0'
'\n\t\t\tProperty: "MaxDampStrengthX", "double", "",0'
'\n\t\t\tProperty: "MaxDampStrengthY", "double", "",0'
'\n\t\t\tProperty: "MaxDampStrengthZ", "double", "",0'
'\n\t\t\tProperty: "PreferedAngleX", "double", "",0'
'\n\t\t\tProperty: "PreferedAngleY", "double", "",0'
'\n\t\t\tProperty: "PreferedAngleZ", "double", "",0'
'\n\t\t\tProperty: "InheritType", "enum", "",0'
'\n\t\t\tProperty: "ScalingActive", "bool", "",0'
)
fw('\n\t\t\tProperty: "ScalingMin", "Vector3D", "",%.15g,%.15g,%.15g' % constraints["sca_min"])
fw('\n\t\t\tProperty: "ScalingMax", "Vector3D", "",%.15g,%.15g,%.15g' % constraints["sca_max"])
fw('\n\t\t\tProperty: "ScalingMinX", "bool", "",%d' % constraints["sca_limit"][0])
fw('\n\t\t\tProperty: "ScalingMinY", "bool", "",%d' % constraints["sca_limit"][1])
fw('\n\t\t\tProperty: "ScalingMinZ", "bool", "",%d' % constraints["sca_limit"][2])
fw('\n\t\t\tProperty: "ScalingMaxX", "bool", "",%d' % constraints["sca_limit"][3])
fw('\n\t\t\tProperty: "ScalingMaxY", "bool", "",%d' % constraints["sca_limit"][4])
fw('\n\t\t\tProperty: "ScalingMaxZ", "bool", "",%d' % constraints["sca_limit"][5])
fw('\n\t\t\tProperty: "GeometricTranslation", "Vector3D", "",0,0,0'
'\n\t\t\tProperty: "GeometricRotation", "Vector3D", "",0,0,0'
'\n\t\t\tProperty: "GeometricScaling", "Vector3D", "",1,1,1'
'\n\t\t\tProperty: "LookAtProperty", "object", ""'
'\n\t\t\tProperty: "UpVectorProperty", "object", ""'
'\n\t\t\tProperty: "Show", "bool", "",1'
'\n\t\t\tProperty: "NegativePercentShapeSupport", "bool", "",1'
'\n\t\t\tProperty: "DefaultAttributeIndex", "int", "",0'
)
if ob and not isinstance(ob, bpy.types.Bone):
# Only mesh objects have color
fw('\n\t\t\tProperty: "Color", "Color", "A",0.8,0.8,0.8'
'\n\t\t\tProperty: "Size", "double", "",100'
'\n\t\t\tProperty: "Look", "enum", "",1'
)
return loc, rot, scale, matrix, matrix_rot
# -------------------------------------------- Armatures
#def write_bone(bone, name, matrix_mod):
def write_bone(my_bone):
fw('\n\tModel: "Model::%s", "Limb" {' % my_bone.fbxName)
fw('\n\t\tVersion: 232')
#~ poseMatrix = write_object_props(my_bone.blenBone, None, None, my_bone.fbxArm.parRelMatrix())[3]
poseMatrix = write_object_props(my_bone.blenBone, pose_bone=my_bone.getPoseBone())[3] # dont apply bone matrices anymore
# Use the same calculation as in write_sub_deformer_skin to compute the global
# transform of the bone for the bind pose.
global_matrix_bone = (my_bone.fbxArm.matrixWorld * my_bone.restMatrix) * mtx4_z90
pose_items.append((my_bone.fbxName, global_matrix_bone))
# fw('\n\t\t\tProperty: "Size", "double", "",%.6f' % ((my_bone.blenData.head['ARMATURESPACE'] - my_bone.blenData.tail['ARMATURESPACE']) * my_bone.fbxArm.parRelMatrix()).length)
fw('\n\t\t\tProperty: "Size", "double", "",1')
#((my_bone.blenData.head['ARMATURESPACE'] * my_bone.fbxArm.matrixWorld) - (my_bone.blenData.tail['ARMATURESPACE'] * my_bone.fbxArm.parRelMatrix())).length)
"""
fw('\n\t\t\tProperty: "LimbLength", "double", "",%.6f' %\
((my_bone.blenBone.head['ARMATURESPACE'] - my_bone.blenBone.tail['ARMATURESPACE']) * my_bone.fbxArm.parRelMatrix()).length)
"""
fw('\n\t\t\tProperty: "LimbLength", "double", "",%.6f' %
(my_bone.blenBone.head_local - my_bone.blenBone.tail_local).length)
#fw('\n\t\t\tProperty: "LimbLength", "double", "",1')
fw('\n\t\t\tProperty: "Color", "ColorRGB", "",0.8,0.8,0.8'
'\n\t\t\tProperty: "Color", "Color", "A",0.8,0.8,0.8'
'\n\t\t}'
'\n\t\tMultiLayer: 0'
'\n\t\tMultiTake: 1'
'\n\t\tShading: Y'
'\n\t\tCulling: "CullingOff"'
'\n\t\tTypeFlags: "Skeleton"'
'\n\t}'
)
def write_camera_switch():
Campbell Barton
committed
Model: "Model::Camera Switcher", "CameraSwitcher" {
Version: 232''')
write_object_props()
Campbell Barton
committed
Property: "Color", "Color", "A",0.8,0.8,0.8
Property: "Camera Index", "Integer", "A+",100
}
MultiLayer: 0
MultiTake: 1
Hidden: "True"
Shading: W
Culling: "CullingOff"
Version: 101
Name: "Model::Camera Switcher"
CameraId: 0
CameraName: 100
CameraIndexName:
}''')
def write_camera_dummy(name, loc, near, far, proj_type, up):
fw('\n\tModel: "Model::%s", "Camera" {' % name)
fw('\n\t\tVersion: 232')
write_object_props(None, loc)
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
fw('\n\t\t\tProperty: "Color", "Color", "A",0.8,0.8,0.8'
'\n\t\t\tProperty: "Roll", "Roll", "A+",0'
'\n\t\t\tProperty: "FieldOfView", "FieldOfView", "A+",40'
'\n\t\t\tProperty: "FieldOfViewX", "FieldOfView", "A+",1'
'\n\t\t\tProperty: "FieldOfViewY", "FieldOfView", "A+",1'
'\n\t\t\tProperty: "OpticalCenterX", "Real", "A+",0'
'\n\t\t\tProperty: "OpticalCenterY", "Real", "A+",0'
'\n\t\t\tProperty: "BackgroundColor", "Color", "A+",0.63,0.63,0.63'
'\n\t\t\tProperty: "TurnTable", "Real", "A+",0'
'\n\t\t\tProperty: "DisplayTurnTableIcon", "bool", "",1'
'\n\t\t\tProperty: "Motion Blur Intensity", "Real", "A+",1'
'\n\t\t\tProperty: "UseMotionBlur", "bool", "",0'
'\n\t\t\tProperty: "UseRealTimeMotionBlur", "bool", "",1'
'\n\t\t\tProperty: "ResolutionMode", "enum", "",0'
'\n\t\t\tProperty: "ApertureMode", "enum", "",2'
'\n\t\t\tProperty: "GateFit", "enum", "",0'
'\n\t\t\tProperty: "FocalLength", "Real", "A+",21.3544940948486'
'\n\t\t\tProperty: "CameraFormat", "enum", "",0'
'\n\t\t\tProperty: "AspectW", "double", "",320'
'\n\t\t\tProperty: "AspectH", "double", "",200'
'\n\t\t\tProperty: "PixelAspectRatio", "double", "",1'
'\n\t\t\tProperty: "UseFrameColor", "bool", "",0'
'\n\t\t\tProperty: "FrameColor", "ColorRGB", "",0.3,0.3,0.3'
'\n\t\t\tProperty: "ShowName", "bool", "",1'
'\n\t\t\tProperty: "ShowGrid", "bool", "",1'
'\n\t\t\tProperty: "ShowOpticalCenter", "bool", "",0'
'\n\t\t\tProperty: "ShowAzimut", "bool", "",1'
'\n\t\t\tProperty: "ShowTimeCode", "bool", "",0'
)
fw('\n\t\t\tProperty: "NearPlane", "double", "",%.6f' % near)
fw('\n\t\t\tProperty: "FarPlane", "double", "",%.6f' % far)
fw('\n\t\t\tProperty: "FilmWidth", "double", "",0.816'
'\n\t\t\tProperty: "FilmHeight", "double", "",0.612'
'\n\t\t\tProperty: "FilmAspectRatio", "double", "",1.33333333333333'
'\n\t\t\tProperty: "FilmSqueezeRatio", "double", "",1'
'\n\t\t\tProperty: "FilmFormatIndex", "enum", "",4'
'\n\t\t\tProperty: "ViewFrustum", "bool", "",1'
'\n\t\t\tProperty: "ViewFrustumNearFarPlane", "bool", "",0'
'\n\t\t\tProperty: "ViewFrustumBackPlaneMode", "enum", "",2'
'\n\t\t\tProperty: "BackPlaneDistance", "double", "",100'
'\n\t\t\tProperty: "BackPlaneDistanceMode", "enum", "",0'
'\n\t\t\tProperty: "ViewCameraToLookAt", "bool", "",1'
'\n\t\t\tProperty: "LockMode", "bool", "",0'
'\n\t\t\tProperty: "LockInterestNavigation", "bool", "",0'
'\n\t\t\tProperty: "FitImage", "bool", "",0'
'\n\t\t\tProperty: "Crop", "bool", "",0'
'\n\t\t\tProperty: "Center", "bool", "",1'
'\n\t\t\tProperty: "KeepRatio", "bool", "",1'
'\n\t\t\tProperty: "BackgroundMode", "enum", "",0'
'\n\t\t\tProperty: "BackgroundAlphaTreshold", "double", "",0.5'
'\n\t\t\tProperty: "ForegroundTransparent", "bool", "",1'
'\n\t\t\tProperty: "DisplaySafeArea", "bool", "",0'
'\n\t\t\tProperty: "SafeAreaDisplayStyle", "enum", "",1'
'\n\t\t\tProperty: "SafeAreaAspectRatio", "double", "",1.33333333333333'
'\n\t\t\tProperty: "Use2DMagnifierZoom", "bool", "",0'
'\n\t\t\tProperty: "2D Magnifier Zoom", "Real", "A+",100'
'\n\t\t\tProperty: "2D Magnifier X", "Real", "A+",50'
'\n\t\t\tProperty: "2D Magnifier Y", "Real", "A+",50'
)
fw('\n\t\t\tProperty: "CameraProjectionType", "enum", "",%i' % proj_type)
fw('\n\t\t\tProperty: "UseRealTimeDOFAndAA", "bool", "",0'
'\n\t\t\tProperty: "UseDepthOfField", "bool", "",0'
'\n\t\t\tProperty: "FocusSource", "enum", "",0'
'\n\t\t\tProperty: "FocusAngle", "double", "",3.5'
'\n\t\t\tProperty: "FocusDistance", "double", "",200'
'\n\t\t\tProperty: "UseAntialiasing", "bool", "",0'
'\n\t\t\tProperty: "AntialiasingIntensity", "double", "",0.77777'
'\n\t\t\tProperty: "UseAccumulationBuffer", "bool", "",0'
'\n\t\t\tProperty: "FrameSamplingCount", "int", "",7'
'\n\t\t}'
'\n\t\tMultiLayer: 0'
'\n\t\tMultiTake: 0'
'\n\t\tHidden: "True"'
'\n\t\tShading: Y'
'\n\t\tCulling: "CullingOff"'
'\n\t\tTypeFlags: "Camera"'
'\n\t\tGeometryVersion: 124'
)
fw('\n\t\tPosition: %.6f,%.6f,%.6f' % loc)
fw('\n\t\tUp: %i,%i,%i' % up)
fw('\n\t\tLookAt: 0,0,0'
'\n\t\tShowInfoOnMoving: 1'
'\n\t\tShowAudio: 0'
'\n\t\tAudioColor: 0,1,0'
'\n\t\tCameraOrthoZoom: 1'
'\n\t}'
)
def write_camera_default():
# This sucks but to match FBX converter its easier to
# write the cameras though they are not needed.
write_camera_dummy('Producer Perspective', (0, 71.3, 287.5), 10, 4000, 0, (0, 1, 0))
write_camera_dummy('Producer Top', (0, 4000, 0), 1, 30000, 1, (0, 0, -1))
write_camera_dummy('Producer Bottom', (0, -4000, 0), 1, 30000, 1, (0, 0, -1))
write_camera_dummy('Producer Front', (0, 0, 4000), 1, 30000, 1, (0, 1, 0))
write_camera_dummy('Producer Back', (0, 0, -4000), 1, 30000, 1, (0, 1, 0))
write_camera_dummy('Producer Right', (4000, 0, 0), 1, 30000, 1, (0, 1, 0))
write_camera_dummy('Producer Left', (-4000, 0, 0), 1, 30000, 1, (0, 1, 0))
def write_camera(my_cam):
Write a blender camera
render = scene.render
width = render.resolution_x
height = render.resolution_y
aspect = width / height
data = my_cam.blenObject.data
# film width & height from mm to inches
filmwidth = data.sensor_width * 0.0393700787
filmheight = data.sensor_height * 0.0393700787
filmaspect = filmwidth / filmheight
# film offset
offsetx = filmwidth * data.shift_x
offsety = filmaspect * filmheight * data.shift_y
fw('\n\tModel: "Model::%s", "Camera" {' % my_cam.fbxName)
fw('\n\t\tVersion: 232')
loc, rot, scale, matrix, matrix_rot = write_object_props(my_cam.blenObject, None, my_cam.parRelMatrix())
fw('\n\t\t\tProperty: "FieldOfView", "FieldOfView", "A+",%.6f' % math.degrees(data.angle_y))
fw('\n\t\t\tProperty: "FieldOfViewX", "FieldOfView", "A+",1'
'\n\t\t\tProperty: "FieldOfViewY", "FieldOfView", "A+",1'
)
fw('\n\t\t\tProperty: "FocalLength", "Number", "A+",%.6f' % data.lens)
fw('\n\t\t\tProperty: "FilmOffsetX", "Number", "A+",%.6f' % offsetx)
fw('\n\t\t\tProperty: "FilmOffsetY", "Number", "A+",%.6f' % offsety)
fw('\n\t\t\tProperty: "BackgroundColor", "Color", "A+",0,0,0'
'\n\t\t\tProperty: "TurnTable", "Real", "A+",0'
'\n\t\t\tProperty: "DisplayTurnTableIcon", "bool", "",1'
'\n\t\t\tProperty: "Motion Blur Intensity", "Real", "A+",1'
'\n\t\t\tProperty: "UseMotionBlur", "bool", "",0'
'\n\t\t\tProperty: "UseRealTimeMotionBlur", "bool", "",1'
'\n\t\t\tProperty: "ResolutionMode", "enum", "",0'
'\n\t\t\tProperty: "ApertureMode", "enum", "",3' # horizontal - Houdini compatible
'\n\t\t\tProperty: "GateFit", "enum", "",2'
'\n\t\t\tProperty: "CameraFormat", "enum", "",0'
)
fw('\n\t\t\tProperty: "AspectW", "double", "",%i' % width)
fw('\n\t\t\tProperty: "AspectH", "double", "",%i' % height)
"""Camera aspect ratio modes.
0 If the ratio mode is eWINDOW_SIZE, both width and height values aren't relevant.
1 If the ratio mode is eFIXED_RATIO, the height value is set to 1.0 and the width value is relative to the height value.
2 If the ratio mode is eFIXED_RESOLUTION, both width and height values are in pixels.
3 If the ratio mode is eFIXED_WIDTH, the width value is in pixels and the height value is relative to the width value.
4 If the ratio mode is eFIXED_HEIGHT, the height value is in pixels and the width value is relative to the height value.
Definition at line 234 of file kfbxcamera.h. """
fw('\n\t\t\tProperty: "PixelAspectRatio", "double", "",1'
'\n\t\t\tProperty: "UseFrameColor", "bool", "",0'
'\n\t\t\tProperty: "FrameColor", "ColorRGB", "",0.3,0.3,0.3'
'\n\t\t\tProperty: "ShowName", "bool", "",1'
'\n\t\t\tProperty: "ShowGrid", "bool", "",1'
'\n\t\t\tProperty: "ShowOpticalCenter", "bool", "",0'
'\n\t\t\tProperty: "ShowAzimut", "bool", "",1'
'\n\t\t\tProperty: "ShowTimeCode", "bool", "",0'
)
fw('\n\t\t\tProperty: "NearPlane", "double", "",%.6f' % (data.clip_start * global_scale))
fw('\n\t\t\tProperty: "FarPlane", "double", "",%.6f' % (data.clip_end * global_scale))
fw('\n\t\t\tProperty: "FilmWidth", "double", "",%.6f' % filmwidth)
fw('\n\t\t\tProperty: "FilmHeight", "double", "",%.6f' % filmheight)
fw('\n\t\t\tProperty: "FilmAspectRatio", "double", "",%.6f' % filmaspect)
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
fw('\n\t\t\tProperty: "FilmSqueezeRatio", "double", "",1'
'\n\t\t\tProperty: "FilmFormatIndex", "enum", "",0'
'\n\t\t\tProperty: "ViewFrustum", "bool", "",1'
'\n\t\t\tProperty: "ViewFrustumNearFarPlane", "bool", "",0'
'\n\t\t\tProperty: "ViewFrustumBackPlaneMode", "enum", "",2'
'\n\t\t\tProperty: "BackPlaneDistance", "double", "",100'
'\n\t\t\tProperty: "BackPlaneDistanceMode", "enum", "",0'
'\n\t\t\tProperty: "ViewCameraToLookAt", "bool", "",1'
'\n\t\t\tProperty: "LockMode", "bool", "",0'
'\n\t\t\tProperty: "LockInterestNavigation", "bool", "",0'
'\n\t\t\tProperty: "FitImage", "bool", "",0'
'\n\t\t\tProperty: "Crop", "bool", "",0'
'\n\t\t\tProperty: "Center", "bool", "",1'
'\n\t\t\tProperty: "KeepRatio", "bool", "",1'
'\n\t\t\tProperty: "BackgroundMode", "enum", "",0'
'\n\t\t\tProperty: "BackgroundAlphaTreshold", "double", "",0.5'
'\n\t\t\tProperty: "ForegroundTransparent", "bool", "",1'
'\n\t\t\tProperty: "DisplaySafeArea", "bool", "",0'
'\n\t\t\tProperty: "SafeAreaDisplayStyle", "enum", "",1'
)
fw('\n\t\t\tProperty: "SafeAreaAspectRatio", "double", "",%.6f' % aspect)
fw('\n\t\t\tProperty: "Use2DMagnifierZoom", "bool", "",0'
'\n\t\t\tProperty: "2D Magnifier Zoom", "Real", "A+",100'
'\n\t\t\tProperty: "2D Magnifier X", "Real", "A+",50'
'\n\t\t\tProperty: "2D Magnifier Y", "Real", "A+",50'
'\n\t\t\tProperty: "CameraProjectionType", "enum", "",0'
'\n\t\t\tProperty: "UseRealTimeDOFAndAA", "bool", "",0'
'\n\t\t\tProperty: "UseDepthOfField", "bool", "",0'
'\n\t\t\tProperty: "FocusSource", "enum", "",0'
'\n\t\t\tProperty: "FocusAngle", "double", "",3.5'
'\n\t\t\tProperty: "FocusDistance", "double", "",200'
'\n\t\t\tProperty: "UseAntialiasing", "bool", "",0'
'\n\t\t\tProperty: "AntialiasingIntensity", "double", "",0.77777'
'\n\t\t\tProperty: "UseAccumulationBuffer", "bool", "",0'
'\n\t\t\tProperty: "FrameSamplingCount", "int", "",7'
)
fw('\n\t\t}')
fw('\n\t\tMultiLayer: 0'
'\n\t\tMultiTake: 0'
'\n\t\tShading: Y'
'\n\t\tCulling: "CullingOff"'
'\n\t\tTypeFlags: "Camera"'
'\n\t\tGeometryVersion: 124'
)
fw('\n\t\tPosition: %.6f,%.6f,%.6f' % loc)
fw('\n\t\tUp: %.6f,%.6f,%.6f' % (matrix_rot * Vector((0.0, 1.0, 0.0)))[:])
fw('\n\t\tLookAt: %.6f,%.6f,%.6f' % (matrix_rot * Vector((0.0, 0.0, -1.0)))[:])
#fw('\n\t\tUp: 0,0,0' )
#fw('\n\t\tLookAt: 0,0,0' )
fw('\n\t\tShowInfoOnMoving: 1')
fw('\n\t\tShowAudio: 0')
fw('\n\t\tAudioColor: 0,1,0')
fw('\n\t\tCameraOrthoZoom: 1')
fw('\n\t}')
def write_light(my_light):
light = my_light.blenObject.data
fw('\n\tModel: "Model::%s", "Light" {' % my_light.fbxName)
fw('\n\t\tVersion: 232')
write_object_props(my_light.blenObject, None, my_light.parRelMatrix())
# Why are these values here twice?????? - oh well, follow the holy sdk's output
# Blender light types match FBX's, funny coincidence, we just need to
# be sure that all unsupported types are made into a point light
#ePOINT,
#eDIRECTIONAL
#eSPOT
light_type_items = {'POINT': 0, 'SUN': 1, 'SPOT': 2, 'HEMI': 3, 'AREA': 4}
light_type = light_type_items[light.type]
if light_type > 2:
light_type = 1 # hemi and area lights become directional
if light.type == 'HEMI':
Campbell Barton
committed
do_light = not (light.use_diffuse or light.use_specular)
do_shadow = False