blender/release/scripts/export_fbx.py
Campbell Barton 798001556a FBX armature + mesh + weights works now.
So it can be used to export walk cycles etc.
Animated armatures also work (import BVH and export as FBX for instance)

Pose data is transformation is key'd on every frame at the moment, so IK's and constraints are applied but blenders keyframes are not used.

at the moment one armature applied multiple meshes wont work properly and armatures cant have transformation.
2007-08-20 23:38:39 +00:00

2036 lines
66 KiB
Python

#!BPY
"""
Name: 'Autodesk FBX (.fbx)...'
Blender: 244
Group: 'Export'
Tooltip: 'Selection to an ASCII Autodesk FBX '
"""
__author__ = "Campbell Barton"
__url__ = ['www.blender.org', 'blenderartists.org']
__version__ = "1.1"
__bpydoc__ = """\
This script is an exporter to the FBX file format.
Usage:
Select the objects you wish to export and run this script from "File->Export" menu.
All objects that can be represented as a mesh (mesh, curve, metaball, surface, text3d)
will be exported as mesh data.
"""
# --------------------------------------------------------------------------
# FBX Export v0.1 by Campbell Barton (AKA Ideasman)
# --------------------------------------------------------------------------
# ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------
import Blender
import BPyObject
import BPyMesh
import BPySys
import BPyMessages
import time
from math import degrees, atan, pi
from Blender.Mathutils import Matrix, Vector, Euler, RotationMatrix, TranslationMatrix
mtx_z90 = RotationMatrix(90, 3, 'z')
mtx_x90 = RotationMatrix(90, 3, 'x')
# testing
mtx_x90 = RotationMatrix( 90, 3, 'x')
mtx_x90n = RotationMatrix(-90, 3, 'x')
mtx_y90 = RotationMatrix( 90, 3, 'y')
mtx_y90n = RotationMatrix(-90, 3, 'y')
mtx_z90 = RotationMatrix( 90, 3, 'z')
mtx_z90n = RotationMatrix(-90, 3, 'z')
mtx4_x90 = RotationMatrix( 90, 4, 'x')
mtx4_x90n = RotationMatrix(-90, 4, 'x')
mtx4_y90 = RotationMatrix( 90, 4, 'y')
mtx4_y90n = RotationMatrix(-90, 4, 'y')
mtx4_z90 = RotationMatrix( 90, 4, 'z')
mtx4_z90n = RotationMatrix(-90, 4, 'z')
XVEC = Vector(1, 0, 0)
XVECN = Vector(-1, 0, 0)
YVEC = Vector(0, 1, 0)
YVECN = Vector(0, -1, 0)
ZVEC = Vector(0, 0, 1)
ZVECN = Vector(0, 0, -1)
ROT_ORDER = [\
(0,1,2),\
(1,2,0),\
(2,0,1),\
(2,1,0),\
(1,0,2),\
(0,2,1),\
]
# Used to add the scene name into the filename without using odd chars
sane_name_mapping_ob = {}
sane_name_mapping_mat = {}
sane_name_mapping_tex = {}
# Change the order rotation is applied.
MATRIX_IDENTITY_3x3 = Matrix([1,0,0],[0,1,0],[0,0,1])
MATRIX_IDENTITY_4x4 = Matrix([1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1])
def eulerRotate(x,y,z, rot_order):
# Clamp all values between 0 and 360, values outside this raise an error.
mats=[RotationMatrix(x%360,3,'x'), RotationMatrix(y%360,3,'y'), RotationMatrix(z%360,3,'z')]
# print rot_order
# Standard BVH multiplication order, apply the rotation in the order Z,X,Y
return (mats[rot_order[2]]*(mats[rot_order[1]]* (mats[rot_order[0]]* MATRIX_IDENTITY_3x3))).toEuler()
def strip_path(p):
return p.split('\\')[-1].split('/')[-1]
# todo - Disallow the name 'Scene' and 'blend_root' - it will bugger things up.
def sane_name(data, dct):
if not data: return None
name = data.name
try: return dct[name]
except: pass
orig_name = name
name = BPySys.cleanName(name)
dct[orig_name] = name
return name
def sane_obname(data): return sane_name(data, sane_name_mapping_ob)
def sane_matname(data): return sane_name(data, sane_name_mapping_mat)
def sane_texname(data): return sane_name(data, sane_name_mapping_tex)
# storage classes
class my_bone_class:
def __init__(self, blenBone, blenArmature, blenMesh, fbxObName):
self.blenName = blenBone.name
self.blenBone = blenBone
self.blenBoneParent = blenBone.parent
self.blenMesh = blenMesh
self.blenArmature = blenArmature
self.restMatrix = blenBone.matrix['ARMATURESPACE']
self.restMatrixInv = self.restMatrix.copy().invert()
self.restMatrixLocal = None # set later, need parent matrix
self.parent = None
self.fbxName = sane_obname(blenBone)
self.fbxObName = fbxObName
# not public
pose = blenArmature.getPose()
self.__pose_bone = pose.bones[self.blenName]
self.__bone_parent = blenBone.parent
self.__anim_poselist = {} # store a list if matricies here, (poseMatrix, head, tail)
def calcRestMatrixLocal(self):
if self.parent:
self.restMatrixLocal = self.restMatrix * self.parent.restMatrix.copy().invert()
else:
self.restMatrixLocal = self.restMatrix.copy()
def setPoseFrame(self, f):
# cache pose info here, frame must be set beforehand
self.__anim_poselist[f] = (\
self.__pose_bone.poseMatrix.copy(),\
self.__pose_bone.head.copy(),\
self.__pose_bone.tail.copy() )
# get pose from frame.
def getPoseMatrix(self, f):
#return mtx4_z90 * self.__pose_bone.poseMatrix.copy()
#return self.__pose_bone.poseMatrix.copy()
return self.__anim_poselist[f][0].copy()
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 getPoseMatrixLocal(self, frame):
if self.parent:
return self.getPoseMatrix(frame) * self.parent.getPoseMatrix(frame).invert()
else:
return self.getPoseMatrix(frame)
#return mtx4_z90 * mat
def getPoseMatrixLocalTip(self):
#print "ASAS"
if self.parent:
vec = self.parent.getPoseTail(frame) - self.parent.getPoseHead(frame)
#vec = self.parent.getPoseHead(frame) - self.parent.getPoseTail(frame)
mat = TranslationMatrix(vec) * self.parent.getPoseMatrix(frame)
#mat = self.parent.getPoseMatrix() * TranslationMatrix(vec).invert()
#print " ASAS"
return mat * self.getPoseMatrix(frame)
else:
return self.getPoseMatrix(frame)
def getPoseMatrixTip(self, frame):
vec = self.getPoseHead(frame) - self.getPoseTail(frame)
return TranslationMatrix(vec) * self.getPoseMatrix(frame)
# This works in 1 test but not for all
def getPoseMatrixLocal_RestRelative(self, frame):
matrix= self.getPoseMatrix(frame)
rest_matrix = self.restMatrix.copy()
if self.parent:
#matrix= matrix * self.parent.getPoseMatrix(frame).invert()
matrix= matrix * self.parent.getPoseMatrixTip(frame).invert()
rest_matrix= rest_matrix * self.parent.restMatrixInv
rest_matrix = mtx4_x90 * rest_matrix
else:
rest_matrix = mtx4_z90n * rest_matrix
return matrix * rest_matrix.invert()
def getPoseMatrix_RestRelative(self, frame):
return self.getPoseMatrix(frame) * self.restMatrix.copy().invert()
def getPoseMatrix_RestRelative_ZROT(self, frame):
# Works for rest bones with no loc/size/rot
# but failes othwrwise.
return (mtx4_z90 * self.getPoseMatrix(frame)) * (mtx4_z90 * self.restMatrix.copy()).invert()
v = Vector(0,0,1) * self.restMatrix.copy().invert().rotationPart()
rest_z90 = RotationMatrix(90, 4, 'r', v)
return (mtx4_z90 * self.getPoseMatrix(frame)) * (rest_z90 * self.restMatrix.copy()).invert()
def getSomeMatrix2(self, frame):
return self.getPoseMatrixLocal(frame) * self.restMatrixLocal.copy()
def getAnimMatrix(self, frame):
if not self.parent:
return mtx4_z90 * self.getPoseMatrix(frame)
else:
return (mtx4_z90 * self.getPoseMatrix(frame)) * (mtx4_z90 * self.parent.getPoseMatrix(frame)).invert()
def mat4x4str(mat):
return '%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f,%.15f' % tuple([ f for v in mat for f in v ])
header_comment = \
'''; FBX 6.1.0 project file
; Created by Blender FBX Exporter
; for support mail: ideasman42@gmail.com
; ----------------------------------------------------
'''
def write_header(file):
file.write(header_comment)
curtime = time.localtime()[0:6]
#
file.write(\
'''FBXHeaderExtension: {
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))
file.write('\nCreationTime: "%.4i-%.2i-%.2i %.2i:%.2i:%.2i:000"' % curtime)
file.write('\nCreator: "Blender3D version %.2f"' % Blender.Get('version'))
def write_scene(file, sce, world,\
EXPORT_APPLY_MODIFIERS=False,\
EXPORT_ANIMATION=True,\
EXPORT_NORMALS_HQ=False):
def object_tx(ob, loc, matrix, matrix_mod = None):
'''
Matrix mod is so armature objects can modify their bone matricies
'''
if isinstance(ob, Blender.Types.BoneType):
# we know we have a matrix
matrix = mtx4_z90 * (matrix_mod * ob.matrix['ARMATURESPACE'])
parent = ob.parent
if parent:
par_matrix = mtx4_z90 * (matrix_mod * parent.matrix['ARMATURESPACE'].copy())
matrix = matrix * par_matrix.copy().invert()
matrix_rot = matrix.rotationPart()
loc = tuple(matrix.translationPart())
scale = tuple(matrix.scalePart())
rot = tuple(matrix_rot.toEuler())
else:
if ob and not matrix: matrix = ob.matrixWorld
matrix_rot = matrix
#if matrix:
# matrix = matrix_scale * matrix
if matrix:
loc = tuple(matrix.translationPart())
scale = tuple(matrix.scalePart())
matrix_rot = matrix.rotationPart()
# Lamps need to be rotated
if ob and ob.type =='Lamp':
matrix_rot = mtx_x90 * matrix.rotationPart()
rot = tuple(matrix_rot.toEuler())
elif ob and ob.type =='Camera':
y = Vector(0,1,0) * matrix_rot
matrix_rot = matrix_rot * RotationMatrix(90, 3, 'r', y)
rot = tuple(matrix_rot.toEuler())
else:
rot = tuple(matrix_rot.toEuler())
else:
if not loc:
loc = 0,0,0
scale = 1,1,1
rot = 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)
# print rot
file.write('\n\t\t\tProperty: "Lcl Translation", "Lcl Translation", "A+",%.15f,%.15f,%.15f' % loc)
file.write('\n\t\t\tProperty: "Lcl Rotation", "Lcl Rotation", "A+",%.15f,%.15f,%.15f' % rot)
file.write('\n\t\t\tProperty: "Lcl Scaling", "Lcl Scaling", "A+",%.15f,%.15f,%.15f' % scale)
return loc, rot, scale, matrix, matrix_rot
def write_object_props(ob=None, loc=None, matrix=None, matrix_mod=None):
# if the type is 0 its an empty otherwise its a mesh
# only difference at the moment is one has a color
file.write('''
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
file.write('''
Property: "RotationOffset", "Vector3D", "",0,0,0
Property: "RotationPivot", "Vector3D", "",0,0,0
Property: "ScalingOffset", "Vector3D", "",0,0,0
Property: "ScalingPivot", "Vector3D", "",0,0,0
Property: "TranslationActive", "bool", "",0
Property: "TranslationMin", "Vector3D", "",0,0,0
Property: "TranslationMax", "Vector3D", "",0,0,0
Property: "TranslationMinX", "bool", "",0
Property: "TranslationMinY", "bool", "",0
Property: "TranslationMinZ", "bool", "",0
Property: "TranslationMaxX", "bool", "",0
Property: "TranslationMaxY", "bool", "",0
Property: "TranslationMaxZ", "bool", "",0
Property: "RotationOrder", "enum", "",0
Property: "RotationSpaceForLimitOnly", "bool", "",0
Property: "AxisLen", "double", "",10
Property: "PreRotation", "Vector3D", "",0,0,0
Property: "PostRotation", "Vector3D", "",0,0,0
Property: "RotationActive", "bool", "",0
Property: "RotationMin", "Vector3D", "",0,0,0
Property: "RotationMax", "Vector3D", "",0,0,0
Property: "RotationMinX", "bool", "",0
Property: "RotationMinY", "bool", "",0
Property: "RotationMinZ", "bool", "",0
Property: "RotationMaxX", "bool", "",0
Property: "RotationMaxY", "bool", "",0
Property: "RotationMaxZ", "bool", "",0
Property: "RotationStiffnessX", "double", "",0
Property: "RotationStiffnessY", "double", "",0
Property: "RotationStiffnessZ", "double", "",0
Property: "MinDampRangeX", "double", "",0
Property: "MinDampRangeY", "double", "",0
Property: "MinDampRangeZ", "double", "",0
Property: "MaxDampRangeX", "double", "",0
Property: "MaxDampRangeY", "double", "",0
Property: "MaxDampRangeZ", "double", "",0
Property: "MinDampStrengthX", "double", "",0
Property: "MinDampStrengthY", "double", "",0
Property: "MinDampStrengthZ", "double", "",0
Property: "MaxDampStrengthX", "double", "",0
Property: "MaxDampStrengthY", "double", "",0
Property: "MaxDampStrengthZ", "double", "",0
Property: "PreferedAngleX", "double", "",0
Property: "PreferedAngleY", "double", "",0
Property: "PreferedAngleZ", "double", "",0
Property: "InheritType", "enum", "",0
Property: "ScalingActive", "bool", "",0
Property: "ScalingMin", "Vector3D", "",1,1,1
Property: "ScalingMax", "Vector3D", "",1,1,1
Property: "ScalingMinX", "bool", "",0
Property: "ScalingMinY", "bool", "",0
Property: "ScalingMinZ", "bool", "",0
Property: "ScalingMaxX", "bool", "",0
Property: "ScalingMaxY", "bool", "",0
Property: "ScalingMaxZ", "bool", "",0
Property: "GeometricTranslation", "Vector3D", "",0,0,0
Property: "GeometricRotation", "Vector3D", "",0,0,0
Property: "GeometricScaling", "Vector3D", "",1,1,1
Property: "LookAtProperty", "object", ""
Property: "UpVectorProperty", "object", ""
Property: "Show", "bool", "",1
Property: "NegativePercentShapeSupport", "bool", "",1
Property: "DefaultAttributeIndex", "int", "",0''')
if ob and type(ob) != Blender.Types.BoneType:
# Only mesh objects have color
file.write('\n\t\t\tProperty: "Color", "Color", "A",0.8,0.8,0.8')
file.write('\n\t\t\tProperty: "Size", "double", "",100')
file.write('\n\t\t\tProperty: "Look", "enum", "",1')
return loc, rot, scale, matrix, matrix_rot
# -------------------------------------------- Armatures
def write_bone(bone, name, matrix_mod):
file.write('\n\tModel: "Model::%s", "Limb" {' % name)
file.write('\n\t\tVersion: 232')
write_object_props(bone, None, None, matrix_mod)
#file.write('\n\t\t\tProperty: "Size", "double", "",%.6f' % ((bone.head['ARMATURESPACE']-bone.tail['ARMATURESPACE']) * matrix_mod).length)
file.write('\n\t\t\tProperty: "Size", "double", "",1')
file.write('\n\t\t\tProperty: "LimbLength", "double", "",%.6f' % (bone.head['ARMATURESPACE']-bone.tail['ARMATURESPACE']).length)
#file.write('\n\t\t\tProperty: "LimbLength", "double", "",1')
file.write('\n\t\t\tProperty: "Color", "ColorRGB", "",0.8,0.8,0.8')
file.write('\n\t\t\tProperty: "Color", "Color", "A",0.8,0.8,0.8')
file.write('\n\t\t}')
file.write('\n\t\tMultiLayer: 0')
file.write('\n\t\tMultiTake: 1')
file.write('\n\t\tShading: Y')
file.write('\n\t\tCulling: "CullingOff"')
file.write('\n\t\tTypeFlags: "Skeleton"')
file.write('\n\t}')
def write_camera_switch():
file.write('''
Model: "Model::Camera Switcher", "CameraSwitcher" {
Version: 232''')
write_object_props()
file.write('''
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):
file.write('\n\tModel: "Model::%s", "Camera" {' % name )
file.write('\n\t\tVersion: 232')
write_object_props(None, loc)
file.write('\n\t\t\tProperty: "Color", "Color", "A",0.8,0.8,0.8')
file.write('\n\t\t\tProperty: "Roll", "Roll", "A+",0')
file.write('\n\t\t\tProperty: "FieldOfView", "FieldOfView", "A+",40')
file.write('\n\t\t\tProperty: "FieldOfViewX", "FieldOfView", "A+",1')
file.write('\n\t\t\tProperty: "FieldOfViewY", "FieldOfView", "A+",1')
file.write('\n\t\t\tProperty: "OpticalCenterX", "Real", "A+",0')
file.write('\n\t\t\tProperty: "OpticalCenterY", "Real", "A+",0')
file.write('\n\t\t\tProperty: "BackgroundColor", "Color", "A+",0.63,0.63,0.63')
file.write('\n\t\t\tProperty: "TurnTable", "Real", "A+",0')
file.write('\n\t\t\tProperty: "DisplayTurnTableIcon", "bool", "",1')
file.write('\n\t\t\tProperty: "Motion Blur Intensity", "Real", "A+",1')
file.write('\n\t\t\tProperty: "UseMotionBlur", "bool", "",0')
file.write('\n\t\t\tProperty: "UseRealTimeMotionBlur", "bool", "",1')
file.write('\n\t\t\tProperty: "ResolutionMode", "enum", "",0')
file.write('\n\t\t\tProperty: "ApertureMode", "enum", "",2')
file.write('\n\t\t\tProperty: "GateFit", "enum", "",0')
file.write('\n\t\t\tProperty: "FocalLength", "Real", "A+",21.3544940948486')
file.write('\n\t\t\tProperty: "CameraFormat", "enum", "",0')
file.write('\n\t\t\tProperty: "AspectW", "double", "",320')
file.write('\n\t\t\tProperty: "AspectH", "double", "",200')
file.write('\n\t\t\tProperty: "PixelAspectRatio", "double", "",1')
file.write('\n\t\t\tProperty: "UseFrameColor", "bool", "",0')
file.write('\n\t\t\tProperty: "FrameColor", "ColorRGB", "",0.3,0.3,0.3')
file.write('\n\t\t\tProperty: "ShowName", "bool", "",1')
file.write('\n\t\t\tProperty: "ShowGrid", "bool", "",1')
file.write('\n\t\t\tProperty: "ShowOpticalCenter", "bool", "",0')
file.write('\n\t\t\tProperty: "ShowAzimut", "bool", "",1')
file.write('\n\t\t\tProperty: "ShowTimeCode", "bool", "",0')
file.write('\n\t\t\tProperty: "NearPlane", "double", "",%.6f' % near)
file.write('\n\t\t\tProperty: "FarPlane", "double", "",%.6f' % far)
file.write('\n\t\t\tProperty: "FilmWidth", "double", "",0.816')
file.write('\n\t\t\tProperty: "FilmHeight", "double", "",0.612')
file.write('\n\t\t\tProperty: "FilmAspectRatio", "double", "",1.33333333333333')
file.write('\n\t\t\tProperty: "FilmSqueezeRatio", "double", "",1')
file.write('\n\t\t\tProperty: "FilmFormatIndex", "enum", "",4')
file.write('\n\t\t\tProperty: "ViewFrustum", "bool", "",1')
file.write('\n\t\t\tProperty: "ViewFrustumNearFarPlane", "bool", "",0')
file.write('\n\t\t\tProperty: "ViewFrustumBackPlaneMode", "enum", "",2')
file.write('\n\t\t\tProperty: "BackPlaneDistance", "double", "",100')
file.write('\n\t\t\tProperty: "BackPlaneDistanceMode", "enum", "",0')
file.write('\n\t\t\tProperty: "ViewCameraToLookAt", "bool", "",1')
file.write('\n\t\t\tProperty: "LockMode", "bool", "",0')
file.write('\n\t\t\tProperty: "LockInterestNavigation", "bool", "",0')
file.write('\n\t\t\tProperty: "FitImage", "bool", "",0')
file.write('\n\t\t\tProperty: "Crop", "bool", "",0')
file.write('\n\t\t\tProperty: "Center", "bool", "",1')
file.write('\n\t\t\tProperty: "KeepRatio", "bool", "",1')
file.write('\n\t\t\tProperty: "BackgroundMode", "enum", "",0')
file.write('\n\t\t\tProperty: "BackgroundAlphaTreshold", "double", "",0.5')
file.write('\n\t\t\tProperty: "ForegroundTransparent", "bool", "",1')
file.write('\n\t\t\tProperty: "DisplaySafeArea", "bool", "",0')
file.write('\n\t\t\tProperty: "SafeAreaDisplayStyle", "enum", "",1')
file.write('\n\t\t\tProperty: "SafeAreaAspectRatio", "double", "",1.33333333333333')
file.write('\n\t\t\tProperty: "Use2DMagnifierZoom", "bool", "",0')
file.write('\n\t\t\tProperty: "2D Magnifier Zoom", "Real", "A+",100')
file.write('\n\t\t\tProperty: "2D Magnifier X", "Real", "A+",50')
file.write('\n\t\t\tProperty: "2D Magnifier Y", "Real", "A+",50')
file.write('\n\t\t\tProperty: "CameraProjectionType", "enum", "",%i' % proj_type)
file.write('\n\t\t\tProperty: "UseRealTimeDOFAndAA", "bool", "",0')
file.write('\n\t\t\tProperty: "UseDepthOfField", "bool", "",0')
file.write('\n\t\t\tProperty: "FocusSource", "enum", "",0')
file.write('\n\t\t\tProperty: "FocusAngle", "double", "",3.5')
file.write('\n\t\t\tProperty: "FocusDistance", "double", "",200')
file.write('\n\t\t\tProperty: "UseAntialiasing", "bool", "",0')
file.write('\n\t\t\tProperty: "AntialiasingIntensity", "double", "",0.77777')
file.write('\n\t\t\tProperty: "UseAccumulationBuffer", "bool", "",0')
file.write('\n\t\t\tProperty: "FrameSamplingCount", "int", "",7')
file.write('\n\t\t}')
file.write('\n\t\tMultiLayer: 0')
file.write('\n\t\tMultiTake: 0')
file.write('\n\t\tHidden: "True"')
file.write('\n\t\tShading: Y')
file.write('\n\t\tCulling: "CullingOff"')
file.write('\n\t\tTypeFlags: "Camera"')
file.write('\n\t\tGeometryVersion: 124')
file.write('\n\t\tPosition: %.6f,%.6f,%.6f' % loc)
file.write('\n\t\tUp: %i,%i,%i' % up)
file.write('\n\t\tLookAt: 0,0,0')
file.write('\n\t\tShowInfoOnMoving: 1')
file.write('\n\t\tShowAudio: 0')
file.write('\n\t\tAudioColor: 0,1,0')
file.write('\n\t\tCameraOrthoZoom: 1')
file.write('\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(ob, name):
'''
Write a blender camera
'''
render = sce.render
width = render.sizeX
height = render.sizeY
aspect = float(width)/height
data = ob.data
file.write('\n\tModel: "Model::%s", "Camera" {' % name )
file.write('\n\t\tVersion: 232')
loc, rot, scale, matrix, matrix_rot = write_object_props(ob)
file.write('\n\t\t\tProperty: "Roll", "Roll", "A+",0')
file.write('\n\t\t\tProperty: "FieldOfView", "FieldOfView", "A+",%.6f' % data.angle)
file.write('\n\t\t\tProperty: "FieldOfViewX", "FieldOfView", "A+",1')
file.write('\n\t\t\tProperty: "FieldOfViewY", "FieldOfView", "A+",1')
file.write('\n\t\t\tProperty: "FocalLength", "Real", "A+",14.0323972702026')
file.write('\n\t\t\tProperty: "OpticalCenterX", "Real", "A+",%.6f' % data.shiftX) # not sure if this is in the correct units?
file.write('\n\t\t\tProperty: "OpticalCenterY", "Real", "A+",%.6f' % data.shiftY) # ditto
file.write('\n\t\t\tProperty: "BackgroundColor", "Color", "A+",0,0,0')
file.write('\n\t\t\tProperty: "TurnTable", "Real", "A+",0')
file.write('\n\t\t\tProperty: "DisplayTurnTableIcon", "bool", "",1')
file.write('\n\t\t\tProperty: "Motion Blur Intensity", "Real", "A+",1')
file.write('\n\t\t\tProperty: "UseMotionBlur", "bool", "",0')
file.write('\n\t\t\tProperty: "UseRealTimeMotionBlur", "bool", "",1')
file.write('\n\t\t\tProperty: "ResolutionMode", "enum", "",0')
file.write('\n\t\t\tProperty: "ApertureMode", "enum", "",2')
file.write('\n\t\t\tProperty: "GateFit", "enum", "",0')
file.write('\n\t\t\tProperty: "CameraFormat", "enum", "",0')
file.write('\n\t\t\tProperty: "AspectW", "double", "",%i' % width)
file.write('\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. '''
file.write('\n\t\t\tProperty: "PixelAspectRatio", "double", "",2')
file.write('\n\t\t\tProperty: "UseFrameColor", "bool", "",0')
file.write('\n\t\t\tProperty: "FrameColor", "ColorRGB", "",0.3,0.3,0.3')
file.write('\n\t\t\tProperty: "ShowName", "bool", "",1')
file.write('\n\t\t\tProperty: "ShowGrid", "bool", "",1')
file.write('\n\t\t\tProperty: "ShowOpticalCenter", "bool", "",0')
file.write('\n\t\t\tProperty: "ShowAzimut", "bool", "",1')
file.write('\n\t\t\tProperty: "ShowTimeCode", "bool", "",0')
file.write('\n\t\t\tProperty: "NearPlane", "double", "",%.6f' % data.clipStart)
file.write('\n\t\t\tProperty: "FarPlane", "double", "",%.6f' % data.clipStart)
file.write('\n\t\t\tProperty: "FilmWidth", "double", "",1.0')
file.write('\n\t\t\tProperty: "FilmHeight", "double", "",1.0')
file.write('\n\t\t\tProperty: "FilmAspectRatio", "double", "",%.6f' % aspect)
file.write('\n\t\t\tProperty: "FilmSqueezeRatio", "double", "",1')
file.write('\n\t\t\tProperty: "FilmFormatIndex", "enum", "",0')
file.write('\n\t\t\tProperty: "ViewFrustum", "bool", "",1')
file.write('\n\t\t\tProperty: "ViewFrustumNearFarPlane", "bool", "",0')
file.write('\n\t\t\tProperty: "ViewFrustumBackPlaneMode", "enum", "",2')
file.write('\n\t\t\tProperty: "BackPlaneDistance", "double", "",100')
file.write('\n\t\t\tProperty: "BackPlaneDistanceMode", "enum", "",0')
file.write('\n\t\t\tProperty: "ViewCameraToLookAt", "bool", "",1')
file.write('\n\t\t\tProperty: "LockMode", "bool", "",0')
file.write('\n\t\t\tProperty: "LockInterestNavigation", "bool", "",0')
file.write('\n\t\t\tProperty: "FitImage", "bool", "",0')
file.write('\n\t\t\tProperty: "Crop", "bool", "",0')
file.write('\n\t\t\tProperty: "Center", "bool", "",1')
file.write('\n\t\t\tProperty: "KeepRatio", "bool", "",1')
file.write('\n\t\t\tProperty: "BackgroundMode", "enum", "",0')
file.write('\n\t\t\tProperty: "BackgroundAlphaTreshold", "double", "",0.5')
file.write('\n\t\t\tProperty: "ForegroundTransparent", "bool", "",1')
file.write('\n\t\t\tProperty: "DisplaySafeArea", "bool", "",0')
file.write('\n\t\t\tProperty: "SafeAreaDisplayStyle", "enum", "",1')
file.write('\n\t\t\tProperty: "SafeAreaAspectRatio", "double", "",%.6f' % aspect)
file.write('\n\t\t\tProperty: "Use2DMagnifierZoom", "bool", "",0')
file.write('\n\t\t\tProperty: "2D Magnifier Zoom", "Real", "A+",100')
file.write('\n\t\t\tProperty: "2D Magnifier X", "Real", "A+",50')
file.write('\n\t\t\tProperty: "2D Magnifier Y", "Real", "A+",50')
file.write('\n\t\t\tProperty: "CameraProjectionType", "enum", "",0')
file.write('\n\t\t\tProperty: "UseRealTimeDOFAndAA", "bool", "",0')
file.write('\n\t\t\tProperty: "UseDepthOfField", "bool", "",0')
file.write('\n\t\t\tProperty: "FocusSource", "enum", "",0')
file.write('\n\t\t\tProperty: "FocusAngle", "double", "",3.5')
file.write('\n\t\t\tProperty: "FocusDistance", "double", "",200')
file.write('\n\t\t\tProperty: "UseAntialiasing", "bool", "",0')
file.write('\n\t\t\tProperty: "AntialiasingIntensity", "double", "",0.77777')
file.write('\n\t\t\tProperty: "UseAccumulationBuffer", "bool", "",0')
file.write('\n\t\t\tProperty: "FrameSamplingCount", "int", "",7')
file.write('\n\t\t}')
file.write('\n\t\tMultiLayer: 0')
file.write('\n\t\tMultiTake: 0')
file.write('\n\t\tShading: Y')
file.write('\n\t\tCulling: "CullingOff"')
file.write('\n\t\tTypeFlags: "Camera"')
file.write('\n\t\tGeometryVersion: 124')
file.write('\n\t\tPosition: %.6f,%.6f,%.6f' % loc)
file.write('\n\t\tUp: %.6f,%.6f,%.6f' % tuple(Vector(0,1,0) * matrix_rot) )
file.write('\n\t\tLookAt: %.6f,%.6f,%.6f' % tuple(Vector(0,0,-1)*matrix_rot) )
#file.write('\n\t\tUp: 0,0,0' )
#file.write('\n\t\tLookAt: 0,0,0' )
file.write('\n\t\tShowInfoOnMoving: 1')
file.write('\n\t\tShowAudio: 0')
file.write('\n\t\tAudioColor: 0,1,0')
file.write('\n\t\tCameraOrthoZoom: 1')
file.write('\n\t}')
def write_light(ob, name):
light = ob.data
file.write('\n\tModel: "Model::%s", "Light" {' % name)
file.write('\n\t\tVersion: 232')
write_object_props(ob)
# 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 = light.type
if light_type > 3: light_type = 0
file.write('\n\t\t\tProperty: "LightType", "enum", "",%i' % light_type)
file.write('\n\t\t\tProperty: "CastLightOnObject", "bool", "",1')
file.write('\n\t\t\tProperty: "DrawVolumetricLight", "bool", "",1')
file.write('\n\t\t\tProperty: "DrawGroundProjection", "bool", "",1')
file.write('\n\t\t\tProperty: "DrawFrontFacingVolumetricLight", "bool", "",0')
file.write('\n\t\t\tProperty: "GoboProperty", "object", ""')
file.write('\n\t\t\tProperty: "Color", "Color", "A+",1,1,1')
file.write('\n\t\t\tProperty: "Intensity", "Intensity", "A+",%.2f' % (light.energy*100))
file.write('\n\t\t\tProperty: "Cone angle", "Cone angle", "A+",%.2f' % light.spotSize)
file.write('\n\t\t\tProperty: "Fog", "Fog", "A+",50')
file.write('\n\t\t\tProperty: "Color", "Color", "A",%.2f,%.2f,%.2f' % tuple(light.col))
file.write('\n\t\t\tProperty: "Intensity", "Intensity", "A+",%.2f' % (light.energy*100))
file.write('\n\t\t\tProperty: "Cone angle", "Cone angle", "A+",%.2f' % light.spotSize)
file.write('\n\t\t\tProperty: "Fog", "Fog", "A+",50')
file.write('\n\t\t\tProperty: "LightType", "enum", "",%i' % light_type)
file.write('\n\t\t\tProperty: "CastLightOnObject", "bool", "",1')
file.write('\n\t\t\tProperty: "DrawGroundProjection", "bool", "",1')
file.write('\n\t\t\tProperty: "DrawFrontFacingVolumetricLight", "bool", "",0')
file.write('\n\t\t\tProperty: "DrawVolumetricLight", "bool", "",1')
file.write('\n\t\t\tProperty: "GoboProperty", "object", ""')
file.write('\n\t\t\tProperty: "DecayType", "enum", "",0')
file.write('\n\t\t\tProperty: "DecayStart", "double", "",%.2f' % light.dist)
file.write('\n\t\t\tProperty: "EnableNearAttenuation", "bool", "",0')
file.write('\n\t\t\tProperty: "NearAttenuationStart", "double", "",0')
file.write('\n\t\t\tProperty: "NearAttenuationEnd", "double", "",0')
file.write('\n\t\t\tProperty: "EnableFarAttenuation", "bool", "",0')
file.write('\n\t\t\tProperty: "FarAttenuationStart", "double", "",0')
file.write('\n\t\t\tProperty: "FarAttenuationEnd", "double", "",0')
file.write('\n\t\t\tProperty: "CastShadows", "bool", "",0')
file.write('\n\t\t\tProperty: "ShadowColor", "ColorRGBA", "",0,0,0,1')
file.write('\n\t\t}')
file.write('\n\t\tMultiLayer: 0')
file.write('\n\t\tMultiTake: 0')
file.write('\n\t\tShading: Y')
file.write('\n\t\tCulling: "CullingOff"')
file.write('\n\t\tTypeFlags: "Light"')
file.write('\n\t\tGeometryVersion: 124')
file.write('\n\t}')
def write_null(ob, name):
# ob can be null
file.write('\n\tModel: "Model::%s", "Null" {' % name)
file.write('\n\t\tVersion: 232')
write_object_props(ob)
file.write('''
}
MultiLayer: 0
MultiTake: 1
Shading: Y
Culling: "CullingOff"
TypeFlags: "Null"
}''')
# Material Settings
if world: world_amb = world.getAmb()
else: world_amb = (0,0,0) # Default value
def write_material(matname, mat):
file.write('\n\tMaterial: "Material::%s", "" {' % matname)
# Todo, add more material Properties.
if mat:
mat_cold = tuple(mat.rgbCol)
mat_cols = tuple(mat.specCol)
#mat_colm = tuple(mat.mirCol) # we wont use the mirror color
mat_colamb = tuple([c for c in world_amb])
mat_dif = mat.ref
mat_amb = mat.amb
mat_hard = (float(mat.hard)-1)/5.10
mat_spec = mat.spec/2.0
mat_alpha = mat.alpha
mat_shadeless = mat.mode & Blender.Material.Modes.SHADELESS
if mat_shadeless:
mat_shader = 'Lambert'
else:
if mat.diffuseShader == Blender.Material.Shaders.DIFFUSE_LAMBERT:
mat_shader = 'Lambert'
else:
mat_shader = 'Phong'
else:
mat_cols = mat_cold = 0.8, 0.8, 0.8
mat_colamb = 0.0,0.0,0.0
# mat_colm
mat_dif = 1.0
mat_amb = 0.5
mat_hard = 20.0
mat_spec = 0.2
mat_alpha = 1.0
mat_shadeless = False
mat_shader = 'Phong'
file.write('\n\t\tVersion: 102')
file.write('\n\t\tShadingModel: "%s"' % mat_shader.lower())
file.write('\n\t\tMultiLayer: 0')
file.write('\n\t\tProperties60: {')
file.write('\n\t\t\tProperty: "ShadingModel", "KString", "", "%s"' % mat_shader)
file.write('\n\t\t\tProperty: "MultiLayer", "bool", "",0')
file.write('\n\t\t\tProperty: "EmissiveColor", "ColorRGB", "",0,0,0')
file.write('\n\t\t\tProperty: "EmissiveFactor", "double", "",1')
file.write('\n\t\t\tProperty: "AmbientColor", "ColorRGB", "",%.1f,%.1f,%.1f' % mat_colamb)
file.write('\n\t\t\tProperty: "AmbientFactor", "double", "",%.1f' % mat_amb)
file.write('\n\t\t\tProperty: "DiffuseColor", "ColorRGB", "",%.1f,%.1f,%.1f' % mat_cold)
file.write('\n\t\t\tProperty: "DiffuseFactor", "double", "",%.1f' % mat_dif)
file.write('\n\t\t\tProperty: "Bump", "Vector3D", "",0,0,0')
file.write('\n\t\t\tProperty: "TransparentColor", "ColorRGB", "",1,1,1')
file.write('\n\t\t\tProperty: "TransparencyFactor", "double", "",0')
if not mat_shadeless:
file.write('\n\t\t\tProperty: "SpecularColor", "ColorRGB", "",%.1f,%.1f,%.1f' % mat_cols)
file.write('\n\t\t\tProperty: "SpecularFactor", "double", "",%.1f' % mat_spec)
file.write('\n\t\t\tProperty: "ShininessExponent", "double", "",80.0')
file.write('\n\t\t\tProperty: "ReflectionColor", "ColorRGB", "",0,0,0')
file.write('\n\t\t\tProperty: "ReflectionFactor", "double", "",1')
file.write('\n\t\t\tProperty: "Emissive", "ColorRGB", "",0,0,0')
file.write('\n\t\t\tProperty: "Ambient", "ColorRGB", "",%.1f,%.1f,%.1f' % mat_colamb)
file.write('\n\t\t\tProperty: "Diffuse", "ColorRGB", "",%.1f,%.1f,%.1f' % mat_cold)
if not mat_shadeless:
file.write('\n\t\t\tProperty: "Specular", "ColorRGB", "",%.1f,%.1f,%.1f' % mat_cols)
file.write('\n\t\t\tProperty: "Shininess", "double", "",%.1f' % mat_hard)
file.write('\n\t\t\tProperty: "Opacity", "double", "",%.1f' % mat_alpha)
if not mat_shadeless:
file.write('\n\t\t\tProperty: "Reflectivity", "double", "",0')
file.write('\n\t\t}')
file.write('\n\t}')
def write_video(texname, tex):
# Same as texture really!
file.write('\n\tVideo: "Video::%s", "Clip" {' % texname)
file.write('''
Type: "Clip"
Properties60: {
Property: "FrameRate", "double", "",0
Property: "LastFrame", "int", "",0
Property: "Width", "int", "",0
Property: "Height", "int", "",0''')
if tex:
fname = tex.filename
fname_strip = strip_path(fname)
else:
fname = fname_strip = ''
file.write('\n\t\t\tProperty: "Path", "charptr", "", "%s"' % fname_strip)
file.write('''
Property: "StartFrame", "int", "",0
Property: "StopFrame", "int", "",0
Property: "PlaySpeed", "double", "",1
Property: "Offset", "KTime", "",0
Property: "InterlaceMode", "enum", "",0
Property: "FreeRunning", "bool", "",0
Property: "Loop", "bool", "",0
Property: "AccessMode", "enum", "",0
}
UseMipMap: 0''')
file.write('\n\t\tFilename: "%s"' % fname_strip)
if fname_strip: fname_strip = '/' + fname_strip
file.write('\n\t\tRelativeFilename: "fbx%s"' % fname_strip) # make relative
file.write('\n\t}')
def write_texture(texname, tex, num):
# if tex == None then this is a dummy tex
file.write('\n\tTexture: "Texture::%s", "TextureVideoClip" {' % texname)
file.write('\n\t\tType: "TextureVideoClip"')
file.write('\n\t\tVersion: 202')
# TODO, rare case _empty_ exists as a name.
file.write('\n\t\tTextureName: "Texture::%s"' % texname)
file.write('''
Properties60: {
Property: "Translation", "Vector", "A+",0,0,0
Property: "Rotation", "Vector", "A+",0,0,0
Property: "Scaling", "Vector", "A+",1,1,1''')
file.write('\n\t\t\tProperty: "Texture alpha", "Number", "A+",%i' % num)
file.write('''
Property: "TextureTypeUse", "enum", "",0
Property: "CurrentTextureBlendMode", "enum", "",1
Property: "UseMaterial", "bool", "",0
Property: "UseMipMap", "bool", "",0
Property: "CurrentMappingType", "enum", "",0
Property: "UVSwap", "bool", "",0
Property: "WrapModeU", "enum", "",0
Property: "WrapModeV", "enum", "",0
Property: "TextureRotationPivot", "Vector3D", "",0,0,0
Property: "TextureScalingPivot", "Vector3D", "",0,0,0
Property: "VideoProperty", "object", ""
}''')
file.write('\n\t\tMedia: "Video::%s"' % texname)
if tex:
fname = tex.filename
file.write('\n\t\tFileName: "%s"' % strip_path(fname))
file.write('\n\t\tRelativeFilename: "fbx/%s"' % strip_path(fname)) # need some make relative command
else:
file.write('\n\t\tFileName: ""')
file.write('\n\t\tRelativeFilename: "fbx"')
file.write('''
ModelUVTranslation: 0,0
ModelUVScaling: 1,1
Texture_Alpha_Source: "None"
Cropping: 0,0,0,0
}''')
def write_deformer_skin(obname):
file.write('\n\tDeformer: "Deformer::Skin %s", "Skin" {' % obname)
file.write('''
Version: 100
MultiLayer: 0
Type: "Skin"
Properties60: {
}
Link_DeformAcuracy: 50
}''')
# in the example was 'Bip01 L Thigh_2'
def write_sub_deformer_skin(obname, group_name, bone, me, matrix_mod):
file.write('\n\tDeformer: "SubDeformer::Cluster %s", "Cluster" {' % group_name)
file.write('''
Version: 100
MultiLayer: 0
Type: "Cluster"
Properties60: {
Property: "SrcModel", "object", ""
Property: "SrcModelReference", "object", ""
}
UserData: "", ""''')
try:
vgroup_data = me.getVertsFromGroup(bone.name, 1)
except:
vgroup_data = []
file.write('\n\t\tIndexes: ')
i = -1
for vg in vgroup_data:
if i == -1:
file.write('%i' % vg[0])
i=0
else:
if i==38:
file.write('\n\t\t')
i=0
file.write(',%i' % vg[0])
i+=1
file.write('\n\t\tWeights: ')
i = -1
for vg in vgroup_data:
if i == -1:
file.write('%.8f' % vg[1])
i=0
else:
if i==38:
file.write('\n\t\t')
i=0
file.write(',%.8f' % vg[1])
i+=1
m = mtx4_z90 * (matrix_mod * bone.matrix['ARMATURESPACE'])
matstr = mat4x4str(m)
matstr_i = mat4x4str(m.invert())
# --- try more here
# It seems fine to have these matricies the same! - worldspace bone or pose locations?
file.write('\n\t\tTransform: %s' % matstr_i) # THIS IS __NOT__ THE GLOBAL MATRIX AS DOCUMENTED :/
file.write('\n\t\tTransformLink: %s' % matstr)
file.write('\n\t}')
def write_mesh(obname, ob, mtx, me, mats, arm, armname):
file.write('\n\tModel: "Model::%s", "Mesh" {' % obname)
file.write('\n\t\tVersion: 232') # newline is added in write_object_props
write_object_props(ob, None, mtx)
file.write('\n\t\t}')
file.write('\n\t\tMultiLayer: 0')
file.write('\n\t\tMultiTake: 1')
file.write('\n\t\tShading: Y')
file.write('\n\t\tCulling: "CullingOff"')
# Write the Real Mesh data here
file.write('\n\t\tVertices: ')
i=-1
for v in me.verts:
if i==-1:
file.write('%.6f,%.6f,%.6f' % tuple(v.co))
i=0
else:
if i==7:
file.write('\n\t\t')
i=0
file.write(',%.6f,%.6f,%.6f'% tuple(v.co))
i+=1
file.write('\n\t\tPolygonVertexIndex: ')
i=-1
for f in me.faces:
fi = [v.index for v in f]
# flip the last index, odd but it looks like
# this is how fbx tells one face from another
fi[-1] = -(fi[-1]+1)
fi = tuple(fi)
if i==-1:
if len(f) == 3: file.write('%i,%i,%i' % fi )
else: file.write('%i,%i,%i,%i' % fi )
i=0
else:
if i==13:
file.write('\n\t\t')
i=0
if len(f) == 3: file.write(',%i,%i,%i' % fi )
else: file.write(',%i,%i,%i,%i' % fi )
i+=1
ed_val = [None, None]
LOOSE = Blender.Mesh.EdgeFlags.LOOSE
for ed in me.edges:
if ed.flag & LOOSE:
ed_val[0] = ed.v1.index
ed_val[1] = -(ed.v2.index+1)
if i==-1:
file.write('%i,%i' % tuple(ed_val) )
i=0
else:
if i==13:
file.write('\n\t\t')
i=0
file.write(',%i,%i' % tuple(ed_val) )
i+=1
del LOOSE
file.write('\n\t\tGeometryVersion: 124')
file.write('''
LayerElementNormal: 0 {
Version: 101
Name: ""
MappingInformationType: "ByVertice"
ReferenceInformationType: "Direct"
Normals: ''')
i=-1
for v in me.verts:
if i==-1:
file.write('%.15f,%.15f,%.15f' % tuple(v.no))
i=0
else:
if i==2:
file.write('\n ')
i=0
file.write(',%.15f,%.15f,%.15f' % tuple(v.no))
i+=1
file.write('\n\t\t}')
# Write VertexColor Layers
collayers = []
if me.vertexColors:
collayers = me.getColorLayerNames()
collayer_orig = me.activeColorLayer
for colindex, collayer in enumerate(collayers):
me.activeColorLayer = collayer
file.write('\n\t\tLayerElementColor: %i {' % colindex)
file.write('\n\t\t\tVersion: 101')
file.write('\n\t\t\tName: "%s"' % collayer)
file.write('''
MappingInformationType: "ByPolygonVertex"
ReferenceInformationType: "IndexToDirect"
Colors: ''')
i = -1
ii = 0 # Count how many Colors we write
for f in me.faces:
for col in f.col:
if i==-1:
file.write('%i,%i,%i' % (col[0], col[1], col[2]))
i=0
else:
if i==7:
file.write('\n\t\t\t\t')
i=0
file.write(',%i,%i,%i' % (col[0], col[1], col[2]))
i+=1
ii+=1 # One more Color
file.write('\n\t\t\tColorIndex: ')
i = -1
for j in xrange(ii):
if i == -1:
file.write('%i' % j)
i=0
else:
if i==55:
file.write('\n\t\t\t\t')
i=0
file.write(',%i' % j)
i+=1
file.write('\n\t\t}')
# Write UV and texture layers.
uvlayers = []
if me.faceUV:
uvlayers = me.getUVLayerNames()
uvlayer_orig = me.activeUVLayer
for uvindex, uvlayer in enumerate(uvlayers):
me.activeUVLayer = uvlayer
file.write('\n\t\tLayerElementUV: %i {' % uvindex)
file.write('\n\t\t\tVersion: 101')
file.write('\n\t\t\tName: "%s"' % uvlayer)
file.write('''
MappingInformationType: "ByPolygonVertex"
ReferenceInformationType: "IndexToDirect"
UV: ''')
i = -1
ii = 0 # Count how many UVs we write
for f in me.faces:
for uv in f.uv:
if i==-1:
file.write('%.6f,%.6f' % tuple(uv))
i=0
else:
if i==7:
file.write('\n ')
i=0
file.write(',%.6f,%.6f' % tuple(uv))
i+=1
ii+=1 # One more UV
file.write('\n\t\t\tUVIndex: ')
i = -1
for j in xrange(ii):
if i == -1:
file.write('%i' % j)
i=0
else:
if i==55:
file.write('\n\t\t\t\t')
i=0
file.write(',%i' % j)
i+=1
file.write('\n\t\t}')
if textures:
file.write('\n\t\tLayerElementTexture: %i {' % uvindex)
file.write('\n\t\t\tVersion: 101')
file.write('\n\t\t\tName: "%s"' % uvlayer)
file.write('''
MappingInformationType: "ByPolygon"
ReferenceInformationType: "IndexToDirect"
BlendMode: "Translucent"
TextureAlpha: 1
TextureId: ''')
i=-1
for f in me.faces:
img_key = f.image
if img_key: img_key = img_key.name
if i==-1:
i=0
file.write( '%s' % texture_mapping_local[img_key])
else:
if i==55:
file.write('\n ')
i=0
file.write(',%s' % texture_mapping_local[img_key])
i+=1
else:
file.write('''
LayerElementTexture: 0 {
Version: 101
Name: ""
MappingInformationType: "NoMappingInformation"
ReferenceInformationType: "IndexToDirect"
BlendMode: "Translucent"
TextureAlpha: 1
TextureId: ''')
file.write('\n\t\t}')
me.activeUVLayer = uvlayer_orig
# Done with UV/textures.
if materials:
file.write('''
LayerElementMaterial: 0 {
Version: 101
Name: ""
MappingInformationType: "ByPolygon"
ReferenceInformationType: "IndexToDirect"
Materials: ''')
# Build a material mapping for this
material_mapping_local = {} # local-index : global index.
for i, mat in enumerate(mats):
if mat:
material_mapping_local[i] = material_mapping[mat.name]
else:
material_mapping_local[i] = 0 # None material is zero for now.
if not material_mapping_local:
material_mapping_local[0] = 0
len_material_mapping_local = len(material_mapping_local)
i=-1
for f in me.faces:
f_mat = f.mat
if f_mat >= len_material_mapping_local:
f_mat = 0
if i==-1:
i=0
file.write( '%s' % material_mapping_local[f_mat])
else:
if i==55:
file.write('\n\t\t\t\t')
i=0
file.write(',%s' % material_mapping_local[f_mat])
i+=1
file.write('\n\t\t}')
file.write('''
Layer: 0 {
Version: 100
LayerElement: {
Type: "LayerElementNormal"
TypedIndex: 0
}''')
if materials:
file.write('''
LayerElement: {
Type: "LayerElementMaterial"
TypedIndex: 0
}''')
# Always write this
if textures:
file.write('''
LayerElement: {
Type: "LayerElementTexture"
TypedIndex: 0
}''')
if me.vertexColors:
file.write('''
LayerElement: {
Type: "LayerElementColor"
TypedIndex: 0
}''')
if me.faceUV:
file.write('''
LayerElement: {
Type: "LayerElementUV"
TypedIndex: 0
}''')
file.write('\n\t\t}')
if len(uvlayers) > 1:
for i in xrange(1, len(uvlayers)):
file.write('\n\t\tLayer: %i {' % i)
file.write('\n\t\t\tVersion: 100')
file.write('''
LayerElement: {
Type: "LayerElementUV"''')
file.write('\n\t\t\t\tTypedIndex: %i' % i)
file.write('\n\t\t\t}')
if textures:
file.write('''
LayerElement: {
Type: "LayerElementTexture"''')
file.write('\n\t\t\t\tTypedIndex: %i' % i)
file.write('\n\t\t\t}')
file.write('\n\t\t}')
if len(collayers) > 1:
# Take into account any UV layers
layer_offset = 0
if uvlayers: layer_offset = len(uvlayers)-1
for i in xrange(layer_offset, len(collayers)+layer_offset):
file.write('\n\t\tLayer: %i {' % i)
file.write('\n\t\t\tVersion: 100')
file.write('''
LayerElement: {
Type: "LayerElementColor"''')
file.write('\n\t\t\t\tTypedIndex: %i' % i)
file.write('\n\t\t\t}')
file.write('\n\t\t}')
file.write('\n\t}')
# add meshes here to clear because they are not used anywhere.
meshes_to_clear = []
ob_meshes = []
ob_lights = []
ob_cameras = []
# in fbx we export bones as children of the mesh
# armatures not a part of a mesh, will be added to ob_arms
ob_bones = []
ob_arms = []
ob_null = [] # emptys
materials = {}
textures = {}
ob_type = None # incase no objects are exported, so as not to raise an error
for ob_base in sce.objects.context:
for ob, mtx in BPyObject.getDerivedObjects(ob_base):
#for ob in [ob_base,]:
ob_type = ob.type
if ob_type == 'Camera':
ob_cameras.append((sane_obname(ob), ob))
elif ob_type == 'Lamp':
ob_lights.append((sane_obname(ob), ob))
elif ob_type == 'Armature':
#ob_arms.append(sane_obname(ob), ob)
ob_arms.append(ob) # replace later.
elif ob_type == 'Empty':
ob_null.append((sane_obname(ob), ob))
else:
if EXPORT_APPLY_MODIFIERS:
me = BPyMesh.getMeshFromObject(ob)
if me:
meshes_to_clear.append( me )
else:
if ob_type == 'Mesh':
me = ob.getData(mesh=1)
else:
me = BPyMesh.getMeshFromObject(ob)
meshes_to_clear.append( me )
if me:
# This WILL modify meshes in blender if EXPORT_APPLY_MODIFIERS is disabled.
# so strictly this is bad. but only in rare cases would it have negative results
# say with dupliverts the objects would rotate a bit differently
if EXPORT_NORMALS_HQ:
BPyMesh.meshCalcNormals(me) # high quality normals nice for realtime engines.
mats = me.materials
for mat in mats:
# 2.44 use mat.lib too for uniqueness
if mat: materials[mat.name] = mat
if me.faceUV:
uvlayer_orig = me.activeUVLayer
for uvlayer in me.getUVLayerNames():
me.activeUVLayer = uvlayer
for f in me.faces:
img = f.image
if img: textures[img.name] = img
me.activeUVLayer = uvlayer_orig
obname = sane_obname(ob)
armob = BPyObject.getObjectArmature(ob)
if armob:
armname = sane_obname(armob)
bones = armob.data.bones.values()
# armatures.append((arm, armname, bones))
# arm_name = BPySys.cleanName(arm.name)
for bone in bones:
#name = sane_obname(arm_name + ' ' + b.name)
#ob_bones.append( (sane_obname(bone), bone, obname, me, armob) )
ob_bones.append( my_bone_class(bone, armob, me, obname) )
else:
armname = None
ob_meshes.append( (obname, ob, mtx, me, mats, armob, armname) )
del ob_type
#print ob_bones
# this sucks a bit, remove all armatures that are used by a mesh.
for obname, ob, mtx, me, mats, arm, armname in ob_meshes:
if arm:
try: ob_arms.remove(arm)
except: pass
# now we have removed, get the unique names
for i, ob in enumerate(ob_arms):
name = sane_obname(ob)
ob_arms[i] = name, ob
for bone in ob.data.bones.values():
#ob_bones.append( (sane_obname(bone), bone, name, None, ob) )
ob_bones.append( my_bone_class(bone, ob, None, name) )
bone_deformer_count = 0 # count how many bones deform a mesh
for mybone in ob_bones:
if mybone.blenBoneParent:
# print "print 'has parent'"
for mybone_parent in ob_bones:
#print mybone.blenBoneParent, mybone_parent.blenBone
# Note 2.45rc2 you can compare bones normally
if mybone.blenBoneParent.name == mybone_parent.blenBone.name and mybone.blenArmature == mybone_parent.blenArmature:
mybone.parent = mybone_parent
#print "FOUND BODE"
break
mybone.calcRestMatrixLocal()
if mybone.blenMesh:
bone_deformer_count += 1
materials = [(sane_matname(mat), mat) for mat in materials.itervalues()]
textures = [(sane_texname(img), img) for img in textures.itervalues()]
materials.sort() # sort by name
textures.sort()
if not materials:
materials = [('null', None)]
material_mapping = {} # blen name : index
if textures:
texture_mapping_local = {None:0} # ditto
i = 0
for texname, tex in textures:
texture_mapping_local[tex.name] = i
i+=1
textures.insert(0, ('_empty_', None))
i = 0
for matname, mat in materials:
if mat: mat = mat.name
material_mapping[mat] = i
i+=1
camera_count = 8
file.write('''
; Object definitions
;------------------------------------------------------------------
Definitions: {
Version: 100
Count: %i''' % (\
1+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 the root model 1 for global settings
del bone_deformer_count
file.write('''
ObjectType: "Model" {
Count: %i
}''' % (\
1+camera_count+\
len(ob_meshes)+\
len(ob_lights)+\
len(ob_cameras)+\
len(ob_arms)+\
len(ob_null)+\
len(ob_bones))) # add 1 for the root model
file.write('''
ObjectType: "Geometry" {
Count: %i
}''' % len(ob_meshes))
if materials:
file.write('''
ObjectType: "Material" {
Count: %i
}''' % len(materials))
if textures:
file.write('''
ObjectType: "Texture" {
Count: %i
}''' % len(textures)) # add 1 for an empty tex
file.write('''
ObjectType: "Video" {
Count: %i
}''' % len(textures)) # add 1 for an empty tex
tmp = 0
for obname, ob, mtx, me, mats, arm, armname in ob_meshes:
if armname:
tmp+=1
for mybone in ob_bones:
if mybone.blenMesh:
tmp += 1
if tmp:
file.write('''
ObjectType: "Deformer" {
Count: %i
}''' % tmp)
del tmp
# we could avoid writing this possibly but for now just write it
"""
file.write('''
ObjectType: "Pose" {
Count: 1
}''')
"""
file.write('''
ObjectType: "GlobalSettings" {
Count: 1
}
}''')
file.write('''
; Object properties
;------------------------------------------------------------------
Objects: {''')
# To comply with other FBX FILES
write_camera_switch()
# Write the null object
write_null(None, 'blend_root')
for obname, ob in ob_null:
write_null(ob, obname)
for obname, ob in ob_arms:
write_null(ob, obname) # armatures are just null's with bone children.
for obname, ob in ob_cameras:
write_camera(ob, obname)
for obname, ob in ob_lights:
write_light(ob, obname)
for obname, ob, mtx, me, mats, arm, armname in ob_meshes:
write_mesh(obname, ob, mtx, me, mats, arm, armname)
#for bonename, bone, obname, me, armob in ob_bones:
for mybone in ob_bones:
#write_bone(bone, bonename, armob.matrixWorld)
write_bone(mybone.blenBone, mybone.fbxName, mybone.blenArmature.matrixWorld)
write_camera_default()
for matname, mat in materials:
write_material(matname, mat)
# 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
# Write armature modifiers
# TODO - add another MODEL? - because of this skin definition.
for obname, ob, mtx, me, mats, arm, armname in ob_meshes:
if armname:
write_deformer_skin(obname)
#for bonename, bone, obname, bone_mesh, armob in ob_bones:
for mybone in ob_bones:
if mybone.blenMesh == me:
#write_sub_deformer_skin(obname, bonename, bone, me, armob.matrixWorld)
write_sub_deformer_skin(obname, mybone.fbxName, mybone.blenBone, mybone.blenMesh, mybone.blenArmature.matrixWorld)
# Write pose's really weired, only needed when an armature and mesh are used together
# each by themselves dont need pose data. for now only pose meshes and bones
"""
file.write('''
Pose: "Pose::BIND_POSES", "BindPose" {
Type: "BindPose"
Version: 100
Properties60: {
}
NbPoseNodes: ''')
file.write(str(\
len(ob_meshes)+\
len(ob_bones)
))
for tmp in (ob_meshes, ob_bones):
for ob in tmp:
file.write('\n\t\tPoseNode: {')
file.write('\n\t\t\tNode: "Model::%s"' % ob[0] ) # the first item is the fbx-name
file.write('\n\t\t\tMatrix: %s' % mat4x4str(object_tx(ob[1], None, None)[3])) # second item is the object or bone
file.write('\n\t\t}')
file.write('\n\t}')
"""
# Finish Writing Objects
# Write global settings
file.write('''
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
}
}
''')
file.write('}')
file.write('''
; Object relations
;------------------------------------------------------------------
Relations: {''')
file.write('\n\tModel: "Model::blend_root", "Null" {\n\t}')
for obname, ob in ob_null:
file.write('\n\tModel: "Model::%s", "Null" {\n\t}' % obname)
for obname, ob in ob_arms:
file.write('\n\tModel: "Model::%s", "Null" {\n\t}' % obname)
for obname, ob, mtx, me, mats, arm, armname in ob_meshes:
file.write('\n\tModel: "Model::%s", "Mesh" {\n\t}' % obname)
# TODO - limbs can have the same name for multiple armatures, should prefix.
#for bonename, bone, obname, me, armob in ob_bones:
for mybone in ob_bones:
file.write('\n\tModel: "Model::%s", "Limb" {\n\t}' % mybone.fbxName)
for obname, ob in ob_cameras:
file.write('\n\tModel: "Model::%s", "Camera" {\n\t}' % obname)
for obname, ob in ob_lights:
file.write('\n\tModel: "Model::%s", "Light" {\n\t}' % obname)
file.write('''
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 in materials:
file.write('\n\tMaterial: "Material::%s", "" {\n\t}' % matname)
if textures:
for texname, tex in textures:
file.write('\n\tTexture: "Texture::%s", "TextureVideoClip" {\n\t}' % texname)
for texname, tex in textures:
file.write('\n\tVideo: "Video::%s", "Clip" {\n\t}' % texname)
# deformers - modifiers
for obname, ob, mtx, me, mats, arm, armname in ob_meshes:
if arm:
file.write('\n\tDeformer: "Deformer::Skin %s", "Skin" {\n\t}' % obname)
#for bonename, bone, obname, me, armob in ob_bones:
for mybone in ob_bones:
if mybone.blenMesh: # is this bone effecting a mesh?
file.write('\n\tDeformer: "SubDeformer::Cluster %s", "Cluster" {\n\t}' % mybone.fbxName)
# This should be at the end
# file.write('\n\tPose: "Pose::BIND_POSES", "BindPose" {\n\t}')
file.write('\n}')
file.write('''
; Object connections
;------------------------------------------------------------------
Connections: {''')
# NOTE - The FBX SDK dosnt care about the order but some importers DO!
# for instance, defining the material->mesh connection
# before the mesh->blend_root crashes cinema4d
# write the fake root node
file.write('\n\tConnect: "OO", "Model::blend_root", "Model::Scene"')
for obname, ob in ob_null:
if ob.parent:
file.write('\n\tConnect: "OO", "Model::%s", "Model::%s"' % (obname, sane_obname(ob.parent)))
else:
file.write('\n\tConnect: "OO", "Model::%s", "Model::blend_root"' % obname)
for obname, ob, mtx, me, mats, arm, armname in ob_meshes:
file.write('\n\tConnect: "OO", "Model::%s", "Model::blend_root"' % obname)
for obname, ob in ob_arms:
file.write('\n\tConnect: "OO", "Model::%s", "Model::blend_root"' % obname)
for obname, ob in ob_cameras:
file.write('\n\tConnect: "OO", "Model::%s", "Model::blend_root"' % obname)
for obname, ob in ob_cameras:
file.write('\n\tConnect: "OO", "Model::%s", "Model::blend_root"' % obname)
for obname, ob in ob_lights:
file.write('\n\tConnect: "OO", "Model::%s", "Model::blend_root"' % obname)
for obname, ob, mtx, me, mats, arm, armname in ob_meshes:
# Connect all materials to all objects, not good form but ok for now.
for mat in mats:
file.write('\n\tConnect: "OO", "Material::%s", "Model::%s"' % (sane_obname(mat), obname))
if textures:
for obname, ob, mtx, me, mats, arm, armname in ob_meshes:
for texname, tex in textures:
file.write('\n\tConnect: "OO", "Texture::%s", "Model::%s"' % (texname, obname))
for texname, tex in textures:
file.write('\n\tConnect: "OO", "Video::%s", "Texture::%s"' % (texname, texname))
for obname, ob, mtx, me, mats, arm, armname in ob_meshes:
if arm:
file.write('\n\tConnect: "OO", "Deformer::Skin %s", "Model::%s"' % (obname, obname))
#for bonename, bone, obname, me, armob in ob_bones:
for mybone in ob_bones:
if mybone.blenMesh:
file.write('\n\tConnect: "OO", "SubDeformer::Cluster %s", "Deformer::Skin %s"' % (mybone.fbxName, mybone.fbxObName))
# limbs -> deformers
# for bonename, bone, obname, me, armob in ob_bones:
for mybone in ob_bones:
if mybone.blenMesh:
file.write('\n\tConnect: "OO", "Model::%s", "SubDeformer::Cluster %s"' % (mybone.fbxName, mybone.fbxName))
#for bonename, bone, obname, me, armob in ob_bones:
for mybone in ob_bones:
if mybone.blenBone.parent:
file.write('\n\tConnect: "OO", "Model::%s", "Model::%s"' % (mybone.fbxName, mybone.parent.fbxName) )
else:
# NOTE, when 'me' is None, the obname is the armature-object,
# the armature object is written as an empty and all root level bones connect to it
file.write('\n\tConnect: "OO", "Model::%s", "Model::%s"' % (mybone.fbxName, mybone.fbxObName) )
file.write('\n}')
if EXPORT_ANIMATION:
# WRITE ANIMATION
render = sce.render
#define KTIME_ONE_SECOND KTime (K_LONGLONG(46186158000))
# 0.5 + val is the same as rounding.
fps = float(render.fps)
def fbx_time(t):
return int(0.5 + ((t/fps) * 46186158000))
# set pose data for all bones
start = render.sFrame
end = render.eFrame
i = start
while i <= end:
Blender.Set('curframe', i)
for mybone in ob_bones:
mybone.setPoseFrame(i)
i+=1
file.write('''
;Takes and animation section
;----------------------------------------------------
Takes: {''')
# todo - support actions
file.write('\n\tCurrent: "Default Take"')
file.write('\n\tTake: "Default Take" {')
# TODO - export multiple actions
# TODO - no attempt at being optinal so far!!
file.write('\n\t\tFileName: "Default_Take.tak"') # ??? - not sure why this is needed
file.write('\n\t\tLocalTime: %i,%i"' % (fbx_time(start), fbx_time(end))) # ??? - not sure why this is needed
file.write('\n\t\tReferenceTime: %i,%i"' % (fbx_time(start), fbx_time(end))) # ??? - not sure why this is needed
file.write('''
;Models animation
;----------------------------------------------------''')
#for bonename, bone, obname, me, armob in ob_bones:
for mybone in ob_bones:
file.write('\n\t\tModel: "Model::%s" {' % mybone.fbxName) # ??? - not sure why this is needed
file.write('\n\t\t\tVersion: 1.1')
file.write('\n\t\t\tChannel: "Transform" {')
# ----------------
for TX_LAYER, TX_CHAN in enumerate('TRS'): # transform, rotate, scale
file.write('\n\t\t\t\tChannel: "%s" {' % TX_CHAN) # translation
for i in xrange(3):
def get_vec(f):
matrix = mybone.getAnimMatrix(f)
if TX_CHAN=='T': return matrix.translationPart()
elif TX_CHAN=='R': return matrix.toEuler()
else: return matrix.scalePart()
file.write('\n\t\t\t\t\tChannel: "%s" {'% ('XYZ'[i])) # translation
file.write('\n\t\t\t\t\t\tDefault: %.15f' % get_vec(start)[i] )
file.write('\n\t\t\t\t\t\tKeyVer: 4004')
file.write('\n\t\t\t\t\t\tKeyCount: %i' % (1 + end - start))
file.write('\n\t\t\t\t\t\tKey: ')
frame = start
while frame <= end:
if frame!=start:
file.write(',')
file.write('\n\t\t\t\t\t\t\t%i,%.15f,C,n' % (fbx_time(frame), get_vec(frame)[i] ))
frame+=1
if i==0: file.write('\n\t\t\t\t\t\tColor: 1,0,0')
elif i==1: file.write('\n\t\t\t\t\t\tColor: 0,1,0')
elif i==2: file.write('\n\t\t\t\t\t\tColor: 0,0,1')
file.write('\n\t\t\t\t\t}')
file.write('\n\t\t\t\t\tLayerType: %i' % (TX_LAYER+1) )
file.write('\n\t\t\t\t}')
# ---------------
file.write('\n\t\t\t}')
file.write('\n\t\t}')
file.write('\n\t}')
file.write('\n}')
else:
# no animation
file.write('\n;Takes and animation section')
file.write('\n;----------------------------------------------------')
file.write('\n')
file.write('\nTakes: {')
file.write('\n\tCurrent: ""')
file.write('\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:
me.verts = None
def write_footer(file, sce, world):
tuple(world.hor)
tuple(world.amb)
has_mist = world.mode & 1
mist_intense, mist_start, mist_end, mist_height = world.mist
render = sce.render
fps = float(render.fps)
def fbx_time(t): return int(0.5 + ((t/fps) * 46186158000))
file.write('\n;Version 5 settings')
file.write('\n;------------------------------------------------------------------')
file.write('\n')
file.write('\nVersion5: {')
file.write('\n\tAmbientRenderSettings: {')
file.write('\n\t\tVersion: 101')
file.write('\n\t\tAmbientLightColor: %.1f,%.1f,%.1f,0' % tuple(world.amb))
file.write('\n\t}')
file.write('\n\tFogOptions: {')
file.write('\n\t\tFlogEnable: %i' % has_mist)
file.write('\n\t\tFogMode: 0')
file.write('\n\t\tFogDensity: %.3f' % mist_intense)
file.write('\n\t\tFogStart: %.3f' % mist_start)
file.write('\n\t\tFogEnd: %.3f' % mist_end)
file.write('\n\t\tFogColor: %.1f,%.1f,%.1f,1' % tuple(world.hor))
file.write('\n\t}')
file.write('\n\tSettings: {')
file.write('\n\t\tFrameRate: "%i"' % int(fps))
file.write('\n\t\tTimeFormat: 1')
file.write('\n\t\tSnapOnFrames: 0')
file.write('\n\t\tReferenceTimeIndex: -1')
file.write('\n\t\tTimeLineStartTime: %i' % fbx_time(render.sFrame))
file.write('\n\t\tTimeLineStopTime: %i' % fbx_time(render.eFrame))
file.write('\n\t}')
file.write('\n\tRendererSetting: {')
file.write('\n\t\tDefaultCamera: "Producer Perspective"')
file.write('\n\t\tDefaultViewingMode: 0')
file.write('\n\t}')
file.write('\n}')
file.write('\n')
# Incase sombody imports this, clean up by clearing global dicts
sane_name_mapping_ob.clear()
sane_name_mapping_mat.clear()
sane_name_mapping_tex.clear()
import bpy
def write(filename,
EXPORT_APPLY_MODIFIERS=False,
EXPORT_ANIMATION=True,
EXPORT_NORMALS_HQ=False):
sce = bpy.data.scenes.active
world = sce.world
file = open(filename, 'w')
write_header(file)
write_scene(file, sce, world,
EXPORT_APPLY_MODIFIERS, EXPORT_ANIMATION, EXPORT_NORMALS_HQ)
write_footer(file, sce, world)
def write_ui(filename):
if not filename.lower().endswith('.fbx'):
filename += '.fbx'
if not BPyMessages.Warning_SaveOver(filename):
return
# ui -------------
EXPORT_APPLY_MODIFIERS = Blender.Draw.Create(0)
EXPORT_NORMALS_HQ = Blender.Draw.Create(0)
EXPORT_ANIMATION = Blender.Draw.Create(1)
pup_block = [\
('Apply Modifiers', EXPORT_APPLY_MODIFIERS, 'Use transformed mesh data from each object. May break vert order for morph targets.'),\
('High Quality Normals', EXPORT_NORMALS_HQ, 'Calculate high quality normals for rendering.'),\
('Animation', EXPORT_ANIMATION, 'Export armature animation between the start and end frames'),\
]
if not Blender.Draw.PupBlock('Export FBX...', pup_block):
return
Blender.Window.EditMode(0)
Blender.Window.WaitCursor(1)
EXPORT_APPLY_MODIFIERS = EXPORT_APPLY_MODIFIERS.val
EXPORT_NORMALS_HQ = EXPORT_NORMALS_HQ.val
EXPORT_ANIMATION = EXPORT_ANIMATION.val
# ------- end UI
Blender.Window.WaitCursor(1)
write(filename, EXPORT_APPLY_MODIFIERS, EXPORT_ANIMATION, EXPORT_NORMALS_HQ)
Blender.Window.WaitCursor(0)
if __name__ == '__main__':
Blender.Window.FileSelector(write_ui, 'Export FBX', Blender.sys.makename(ext='.fbx'))
#write('/scratch/test.fbx')