Newer
Older
# (b'Geometry', b'KFbxMesh')
fbx_templates = {}
def _():
if fbx_defs is not None:
for fbx_def in fbx_defs.elems:
if fbx_def.id == b'ObjectType':
for fbx_subdef in fbx_def.elems:
if fbx_subdef.id == b'PropertyTemplate':
assert(fbx_def.props_type == b'S')
assert(fbx_subdef.props_type == b'S')
# (b'Texture', b'KFbxFileTexture') - eg.
key = fbx_def.props[0], fbx_subdef.props[0]
fbx_templates[key] = fbx_subdef
_(); del _
def fbx_template_get(key):
Bastien Montagne
committed
ret = fbx_templates.get(key, fbx_elem_nil)
if ret is None:
# Newest FBX (7.4 and above) use no more 'K' in their type names...
key = (key[0], key[1][1:])
return fbx_templates.get(key, fbx_elem_nil)
return ret
# ----
# Build FBX node-table
def _():
for fbx_obj in fbx_nodes.elems:
# TODO, investigate what other items after first 3 may be
assert(fbx_obj.props_type[:3] == b'LSS')
fbx_uuid = elem_uuid(fbx_obj)
fbx_table_nodes[fbx_uuid] = [fbx_obj, None]
_(); del _
# ----
# Load in the data
# http://download.autodesk.com/us/fbx/20112/FBX_SDK_HELP/index.html?url=
# WS73099cc142f487551fea285e1221e4f9ff8-7fda.htm,topicNumber=d0e6388
fbx_connection_map = {}
fbx_connection_map_reverse = {}
def _():
for fbx_link in fbx_connections.elems:
c_type = fbx_link.props[0]
if fbx_link.props_type[1:3] == b'LL':
c_src, c_dst = fbx_link.props[1:3]
fbx_connection_map.setdefault(c_src, []).append((c_dst, fbx_link))
fbx_connection_map_reverse.setdefault(c_dst, []).append((c_src, fbx_link))
_(); del _
# ----
# Load mesh data
def _():
fbx_tmpl = fbx_template_get((b'Geometry', b'KFbxMesh'))
for fbx_uuid, fbx_item in fbx_table_nodes.items():
fbx_obj, blen_data = fbx_item
if fbx_obj.id != b'Geometry':
continue
if fbx_obj.props[-1] == b'Mesh':
assert(blen_data is None)
Jens Ch. Restemeier
committed
fbx_item[1] = blen_read_geom(fbx_tmpl, fbx_obj, settings)
_(); del _
# ----
# Load material data
def _():
fbx_tmpl = fbx_template_get((b'Material', b'KFbxSurfacePhong'))
# b'KFbxSurfaceLambert'
for fbx_uuid, fbx_item in fbx_table_nodes.items():
fbx_obj, blen_data = fbx_item
if fbx_obj.id != b'Material':
continue
assert(blen_data is None)
Jens Ch. Restemeier
committed
fbx_item[1] = blen_read_material(fbx_tmpl, fbx_obj, settings)
_(); del _
# ----
# Load image data
def _():
fbx_tmpl = fbx_template_get((b'Texture', b'KFbxFileTexture'))
for fbx_uuid, fbx_item in fbx_table_nodes.items():
fbx_obj, blen_data = fbx_item
if fbx_obj.id != b'Texture':
continue
Jens Ch. Restemeier
committed
fbx_item[1] = blen_read_texture(fbx_tmpl, fbx_obj, basedir, settings)
_(); del _
# ----
# Load camera data
def _():
fbx_tmpl = fbx_template_get((b'NodeAttribute', b'KFbxCamera'))
for fbx_uuid, fbx_item in fbx_table_nodes.items():
fbx_obj, blen_data = fbx_item
if fbx_obj.id != b'NodeAttribute':
continue
if fbx_obj.props[-1] == b'Camera':
assert(blen_data is None)
fbx_item[1] = blen_read_camera(fbx_tmpl, fbx_obj, global_scale)
_(); del _
# ----
# Load lamp data
def _():
fbx_tmpl = fbx_template_get((b'NodeAttribute', b'KFbxLight'))
for fbx_uuid, fbx_item in fbx_table_nodes.items():
fbx_obj, blen_data = fbx_item
if fbx_obj.id != b'NodeAttribute':
continue
if fbx_obj.props[-1] == b'Light':
assert(blen_data is None)
fbx_item[1] = blen_read_light(fbx_tmpl, fbx_obj, global_scale)
# ----
# Connections
def connection_filter_ex(fbx_uuid, fbx_id, dct):
return [(c_found[0], c_found[1], c_type)
for (c_uuid, c_type) in dct.get(fbx_uuid, ())
# 0 is used for the root node, which isnt in fbx_table_nodes
for c_found in (() if c_uuid is 0 else (fbx_table_nodes.get(c_uuid, (None, None)),))
if (fbx_id is None) or (c_found[0] and c_found[0].id == fbx_id)]
def connection_filter_forward(fbx_uuid, fbx_id):
return connection_filter_ex(fbx_uuid, fbx_id, fbx_connection_map)
def connection_filter_reverse(fbx_uuid, fbx_id):
return connection_filter_ex(fbx_uuid, fbx_id, fbx_connection_map_reverse)
Bastien Montagne
committed
# -- temporary helper hierarchy to build armatures and objects from
# lookup from uuid to helper node. Used to build parent-child relations and later to look up animated nodes.
fbx_helper_nodes = {}
def _():
# We build an intermediate hierarchy used to:
# - Calculate and store bone orientation correction matrices. The same matrices will be reused for animation.
# - Find/insert armature nodes.
# - Filter leaf bones.
Bastien Montagne
committed
# create scene root
fbx_helper_nodes[0] = root_helper = FbxImportHelperNode(None, None, None, False)
root_helper.is_root = True
# add fbx nodes
fbx_tmpl = fbx_template_get((b'Model', b'KFbxNode'))
for a_uuid, a_item in fbx_table_nodes.items():
Bastien Montagne
committed
if fbx_obj is None or fbx_obj.id != b'Model':
continue
Bastien Montagne
committed
fbx_props = (elem_find_first(fbx_obj, b'Properties70'),
elem_find_first(fbx_tmpl, b'Properties70', fbx_elem_nil))
assert(fbx_props[0] is not None)
Bastien Montagne
committed
transform_data = blen_read_object_transform_preprocess(fbx_props, fbx_obj, Matrix(), use_prepost_rot)
is_bone = fbx_obj.props[2] in {b'LimbNode'} # Note: 'Root' "bones" are handled as (armature) objects.
Bastien Montagne
committed
fbx_helper_nodes[a_uuid] = FbxImportHelperNode(fbx_obj, bl_data, transform_data, is_bone)
# add parent-child relations and add blender data to the node
for fbx_link in fbx_connections.elems:
if fbx_link.props[0] != b'OO':
continue
if fbx_link.props_type[1:3] == b'LL':
c_src, c_dst = fbx_link.props[1:3]
parent = fbx_helper_nodes.get(c_dst)
if parent is None:
continue
Bastien Montagne
committed
child = fbx_helper_nodes.get(c_src)
if child is None:
# add blender data (meshes, lights, cameras, etc.) to a helper node
fbx_sdata, bl_data = p_item = fbx_table_nodes.get(c_src, (None, None))
if fbx_sdata is None:
continue
Bastien Montagne
committed
if fbx_sdata.id in {b'Material', b'Texture', b'Video'}:
continue
Bastien Montagne
committed
parent.bl_data = bl_data
else:
# set parent
child.parent = parent
Bastien Montagne
committed
# find armatures (either an empty below a bone or a new node inserted at the bone
root_helper.find_armatures()
Bastien Montagne
committed
# mark nodes that have bone children
root_helper.find_bone_children()
Bastien Montagne
committed
# mark nodes that need a bone to attach child-bones to
root_helper.find_fake_bones()
# mark leaf nodes that are only required to mark the end of their parent bone
if settings.ignore_leaf_bones:
root_helper.mark_leaf_bones()
# What a mess! Some bones have several BindPoses, some have none, clusters contain a bind pose as well,
# and you can have several clusters per bone!
Bastien Montagne
committed
# Maybe some conversion can be applied to put them all into the same frame of reference?
# get the bind pose from pose elements
for a_uuid, a_item in fbx_table_nodes.items():
Bastien Montagne
committed
if fbx_obj is None:
continue
Bastien Montagne
committed
if fbx_obj.id != b'Pose':
continue
if fbx_obj.props[2] != b'BindPose':
continue
for fbx_pose_node in fbx_obj.elems:
if fbx_pose_node.id != b'PoseNode':
continue
node_elem = elem_find_first(fbx_pose_node, b'Node')
node = elem_uuid(node_elem)
matrix_elem = elem_find_first(fbx_pose_node, b'Matrix')
matrix = array_to_matrix4(matrix_elem.props[0]) if matrix_elem else None
bone = fbx_helper_nodes.get(node)
if bone and matrix:
# Store the matrix in the helper node.
# There may be several bind pose matrices for the same node, but in tests they seem to be identical.
Bastien Montagne
committed
bone.bind_matrix = matrix # global space
# get clusters and bind pose
for helper_uuid, helper_node in fbx_helper_nodes.items():
if not helper_node.is_bone:
Bastien Montagne
committed
for cluster_uuid, cluster_link in fbx_connection_map.get(helper_uuid, ()):
if cluster_link.props[0] != b'OO':
continue
fbx_cluster, _ = fbx_table_nodes.get(cluster_uuid, (None, None))
if fbx_cluster is None or fbx_cluster.id != b'Deformer' or fbx_cluster.props[2] != b'Cluster':
continue
Bastien Montagne
committed
# Get the bind pose from the cluster:
tx_mesh_elem = elem_find_first(fbx_cluster, b'Transform', default=None)
tx_mesh = array_to_matrix4(tx_mesh_elem.props[0]) if tx_mesh_elem else Matrix()
tx_bone_elem = elem_find_first(fbx_cluster, b'TransformLink', default=None)
tx_bone = array_to_matrix4(tx_bone_elem.props[0]) if tx_bone_elem else None
Bastien Montagne
committed
tx_arm_elem = elem_find_first(fbx_cluster, b'TransformAssociateModel', default=None)
tx_arm = array_to_matrix4(tx_arm_elem.props[0]) if tx_arm_elem else Matrix()
Bastien Montagne
committed
mesh_matrix = tx_mesh
armature_matrix = tx_arm
Bastien Montagne
committed
if tx_bone:
mesh_matrix = tx_bone * mesh_matrix
helper_node.bind_matrix = tx_bone # overwrite the bind matrix
Bastien Montagne
committed
# Get the meshes driven by this cluster: (Shouldn't that be only one?)
meshes = set()
for skin_uuid, skin_link in fbx_connection_map.get(cluster_uuid):
if skin_link.props[0] != b'OO':
Bastien Montagne
committed
fbx_skin, _ = fbx_table_nodes.get(skin_uuid, (None, None))
if fbx_skin is None or fbx_skin.id != b'Deformer' or fbx_skin.props[2] != b'Skin':
continue
Bastien Montagne
committed
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
for mesh_uuid, mesh_link in fbx_connection_map.get(skin_uuid):
if mesh_link.props[0] != b'OO':
continue
fbx_mesh, _ = fbx_table_nodes.get(mesh_uuid, (None, None))
if fbx_mesh is None or fbx_mesh.id != b'Geometry' or fbx_mesh.props[2] != b'Mesh':
continue
for object_uuid, object_link in fbx_connection_map.get(mesh_uuid):
if object_link.props[0] != b'OO':
continue
mesh_node = fbx_helper_nodes[object_uuid]
if mesh_node:
# ----
# If we get a valid mesh matrix (in bone space), store armature and mesh global matrices, we need to set temporarily
# both objects to those matrices when actually binding them via the modifier.
# Note we assume all bones were bound with the same mesh/armature (global) matrix, we do not support otherwise
# in Blender anyway!
mesh_node.armature_setup = (mesh_matrix, armature_matrix)
meshes.add(mesh_node)
helper_node.clusters.append((fbx_cluster, meshes))
# convert bind poses from global space into local space
root_helper.make_bind_pose_local()
# collect armature meshes
root_helper.collect_armature_meshes()
Bastien Montagne
committed
# find the correction matrices to align FBX objects with their Blender equivalent
root_helper.find_correction_matrix(settings)
Bastien Montagne
committed
# build the Object/Armature/Bone hierarchy
root_helper.build_hierarchy(fbx_tmpl, settings, scene)
# root_helper.print_info(0)
_(); del _
# We can handle shapes.
blend_shape_channels = {} # We do not need Shapes themselves, but keyblocks, for anim.
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
def _():
fbx_tmpl = fbx_template_get((b'Geometry', b'KFbxShape'))
for s_uuid, s_item in fbx_table_nodes.items():
fbx_sdata, bl_sdata = s_item = fbx_table_nodes.get(s_uuid, (None, None))
if fbx_sdata is None or fbx_sdata.id != b'Geometry' or fbx_sdata.props[2] != b'Shape':
continue
# shape -> blendshapechannel -> blendshape -> mesh.
for bc_uuid, bc_ctype in fbx_connection_map.get(s_uuid, ()):
if bc_ctype.props[0] != b'OO':
continue
fbx_bcdata, _bl_bcdata = fbx_table_nodes.get(bc_uuid, (None, None))
if fbx_bcdata is None or fbx_bcdata.id != b'Deformer' or fbx_bcdata.props[2] != b'BlendShapeChannel':
continue
meshes = []
objects = []
for bs_uuid, bs_ctype in fbx_connection_map.get(bc_uuid, ()):
if bs_ctype.props[0] != b'OO':
continue
fbx_bsdata, _bl_bsdata = fbx_table_nodes.get(bs_uuid, (None, None))
if fbx_bsdata is None or fbx_bsdata.id != b'Deformer' or fbx_bsdata.props[2] != b'BlendShape':
continue
for m_uuid, m_ctype in fbx_connection_map.get(bs_uuid, ()):
if m_ctype.props[0] != b'OO':
continue
fbx_mdata, bl_mdata = fbx_table_nodes.get(m_uuid, (None, None))
if fbx_mdata is None or fbx_mdata.id != b'Geometry' or fbx_mdata.props[2] != b'Mesh':
continue
# Blenmeshes are assumed already created at that time!
assert(isinstance(bl_mdata, bpy.types.Mesh))
# And we have to find all objects using this mesh!
objects = []
for o_uuid, o_ctype in fbx_connection_map.get(m_uuid, ()):
if o_ctype.props[0] != b'OO':
continue
Bastien Montagne
committed
node = fbx_helper_nodes[o_uuid]
if node:
objects.append(node)
meshes.append((bl_mdata, objects))
# BlendShape deformers are only here to connect BlendShapeChannels to meshes, nothing else to do.
# keyblocks is a list of tuples (mesh, keyblock) matching that shape/blendshapechannel, for animation.
Bastien Montagne
committed
keyblocks = blen_read_shape(fbx_tmpl, fbx_sdata, fbx_bcdata, meshes, scene)
blend_shape_channels[bc_uuid] = keyblocks
_(); del _
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
# Animation!
def _():
fbx_tmpl_astack = fbx_template_get((b'AnimationStack', b'FbxAnimStack'))
fbx_tmpl_alayer = fbx_template_get((b'AnimationLayer', b'FbxAnimLayer'))
stacks = {}
# AnimationStacks.
for as_uuid, fbx_asitem in fbx_table_nodes.items():
fbx_asdata, _blen_data = fbx_asitem
if fbx_asdata.id != b'AnimationStack' or fbx_asdata.props[2] != b'':
continue
stacks[as_uuid] = (fbx_asitem, {})
# AnimationLayers (mixing is completely ignored for now, each layer results in an independent set of actions).
def get_astacks_from_alayer(al_uuid):
for as_uuid, as_ctype in fbx_connection_map.get(al_uuid, ()):
if as_ctype.props[0] != b'OO':
continue
fbx_asdata, _bl_asdata = fbx_table_nodes.get(as_uuid, (None, None))
if (fbx_asdata is None or fbx_asdata.id != b'AnimationStack' or
fbx_asdata.props[2] != b'' or as_uuid not in stacks):
continue
yield as_uuid
for al_uuid, fbx_alitem in fbx_table_nodes.items():
fbx_aldata, _blen_data = fbx_alitem
if fbx_aldata.id != b'AnimationLayer' or fbx_aldata.props[2] != b'':
continue
for as_uuid in get_astacks_from_alayer(al_uuid):
_fbx_asitem, alayers = stacks[as_uuid]
alayers[al_uuid] = (fbx_alitem, {})
# AnimationCurveNodes (also the ones linked to actual animated data!).
curvenodes = {}
for acn_uuid, fbx_acnitem in fbx_table_nodes.items():
fbx_acndata, _blen_data = fbx_acnitem
if fbx_acndata.id != b'AnimationCurveNode' or fbx_acndata.props[2] != b'':
continue
cnode = curvenodes[acn_uuid] = {}
items = []
for n_uuid, n_ctype in fbx_connection_map.get(acn_uuid, ()):
if n_ctype.props[0] != b'OP':
continue
lnk_prop = n_ctype.props[3]
if lnk_prop in {b'Lcl Translation', b'Lcl Rotation', b'Lcl Scaling'}:
# n_uuid can (????) be linked to root '0' node, instead of a mere object node... See T41712.
Bastien Montagne
committed
ob = fbx_helper_nodes.get(n_uuid, None)
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
if ob is None:
continue
items.append((ob, lnk_prop))
elif lnk_prop == b'DeformPercent': # Shape keys.
keyblocks = blend_shape_channels.get(n_uuid)
if keyblocks is None:
continue
items += [(kb, lnk_prop) for kb in keyblocks]
for al_uuid, al_ctype in fbx_connection_map.get(acn_uuid, ()):
if al_ctype.props[0] != b'OO':
continue
fbx_aldata, _blen_aldata = fbx_alitem = fbx_table_nodes.get(al_uuid, (None, None))
if fbx_aldata is None or fbx_aldata.id != b'AnimationLayer' or fbx_aldata.props[2] != b'':
continue
for as_uuid in get_astacks_from_alayer(al_uuid):
_fbx_alitem, anim_items = stacks[as_uuid][1][al_uuid]
assert(_fbx_alitem == fbx_alitem)
for item, item_prop in items:
# No need to keep curvenode FBX data here, contains nothing useful for us.
anim_items.setdefault(item, {})[acn_uuid] = (cnode, item_prop)
# AnimationCurves (real animation data).
for ac_uuid, fbx_acitem in fbx_table_nodes.items():
fbx_acdata, _blen_data = fbx_acitem
if fbx_acdata.id != b'AnimationCurve' or fbx_acdata.props[2] != b'':
continue
for acn_uuid, acn_ctype in fbx_connection_map.get(ac_uuid, ()):
if acn_ctype.props[0] != b'OP':
continue
fbx_acndata, _bl_acndata = fbx_table_nodes.get(acn_uuid, (None, None))
if (fbx_acndata is None or fbx_acndata.id != b'AnimationCurveNode' or
fbx_acndata.props[2] != b'' or acn_uuid not in curvenodes):
continue
# Note this is an infamous simplification of the compound props stuff,
# seems to be standard naming but we'll probably have to be smarter to handle more exotic files?
channel = {b'd|X': 0, b'd|Y': 1, b'd|Z': 2, b'd|DeformPercent': 0}.get(acn_ctype.props[3], None)
if channel is None:
continue
curvenodes[acn_uuid][ac_uuid] = (fbx_acitem, channel)
# And now that we have sorted all this, apply animations!
Bastien Montagne
committed
blen_read_animations(fbx_tmpl_astack, fbx_tmpl_alayer, stacks, scene)
def _():
# link Material's to Geometry (via Model's)
for fbx_uuid, fbx_item in fbx_table_nodes.items():
fbx_obj, blen_data = fbx_item
if fbx_obj.id != b'Geometry':
continue
mesh = fbx_table_nodes.get(fbx_uuid, (None, None))[1]
# can happen in rare cases
if mesh is None:
continue
for (fbx_lnk,
fbx_lnk_item,
fbx_lnk_type) in connection_filter_forward(fbx_uuid, b'Model'):
# link materials
fbx_lnk_uuid = elem_uuid(fbx_lnk)
for (fbx_lnk_material,
material,
fbx_lnk_material_type) in connection_filter_reverse(fbx_lnk_uuid, b'Material'):
mesh.materials.append(material)
Bastien Montagne
committed
# We have to validate mesh polygons' mat_idx, see T41015!
# Some FBX seem to have an extra 'default' material which is not defined in FBX file.
if mesh.validate_material_indices():
print("WARNING: mesh '%s' had invalid material indices, those were reset to first material" % mesh.name)
_(); del _
def _():
Campbell Barton
committed
material_images = {}
fbx_tmpl = fbx_template_get((b'Material', b'KFbxSurfacePhong'))
# b'KFbxSurfaceLambert'
# textures that use this material
def texture_bumpfac_get(fbx_obj):
assert(fbx_obj.id == b'Material')
fbx_props = (elem_find_first(fbx_obj, b'Properties70'),
elem_find_first(fbx_tmpl, b'Properties70', fbx_elem_nil))
assert(fbx_props[0] is not None)
# (x / 7.142) is only a guess, cycles usable range is (0.0 -> 0.5)
return elem_props_get_number(fbx_props, b'BumpFactor', 2.5) / 7.142
def texture_mapping_get(fbx_obj):
assert(fbx_obj.id == b'Texture')
fbx_props = (elem_find_first(fbx_obj, b'Properties70'),
elem_find_first(fbx_tmpl, b'Properties70', fbx_elem_nil))
assert(fbx_props[0] is not None)
return (elem_props_get_vector_3d(fbx_props, b'Translation', (0.0, 0.0, 0.0)),
elem_props_get_vector_3d(fbx_props, b'Rotation', (0.0, 0.0, 0.0)),
elem_props_get_vector_3d(fbx_props, b'Scaling', (1.0, 1.0, 1.0)),
(bool(elem_props_get_enum(fbx_props, b'WrapModeU', 0)),
bool(elem_props_get_enum(fbx_props, b'WrapModeV', 0))))
Campbell Barton
committed
if not use_cycles:
# Simple function to make a new mtex and set defaults
def material_mtex_new(material, image, tex_map):
Campbell Barton
committed
tex = texture_cache.get(image)
if tex is None:
tex = bpy.data.textures.new(name=image.name, type='IMAGE')
tex.image = image
texture_cache[image] = tex
# copy custom properties from image object to texture
for key, value in image.items():
tex[key] = value
# delete custom properties on the image object
for key in image.keys():
del image[key]
Campbell Barton
committed
mtex = material.texture_slots.add()
mtex.texture = tex
mtex.texture_coords = 'UV'
mtex.use_map_color_diffuse = False
# No rotation here...
mtex.offset[:] = tex_map[0]
mtex.scale[:] = tex_map[2]
Campbell Barton
committed
return mtex
for fbx_uuid, fbx_item in fbx_table_nodes.items():
fbx_obj, blen_data = fbx_item
if fbx_obj.id != b'Material':
continue
material = fbx_table_nodes.get(fbx_uuid, (None, None))[1]
for (fbx_lnk,
image,
fbx_lnk_type) in connection_filter_reverse(fbx_uuid, b'Texture'):
if use_cycles:
if fbx_lnk_type.props[0] == b'OP':
lnk_type = fbx_lnk_type.props[3]
ma_wrap = cycles_material_wrap_map[material]
# tx/rot/scale
tex_map = texture_mapping_get(fbx_lnk)
if (tex_map[0] == (0.0, 0.0, 0.0) and
tex_map[1] == (0.0, 0.0, 0.0) and
tex_map[2] == (1.0, 1.0, 1.0) and
tex_map[3] == (False, False)):
use_mapping = False
else:
use_mapping = True
tex_map_kw = {
"translation": tex_map[0],
"rotation": [-i for i in tex_map[1]],
"scale": [((1.0 / i) if i != 0.0 else 1.0) for i in tex_map[2]],
"clamp": tex_map[3],
if lnk_type == b'DiffuseColor':
ma_wrap.diffuse_image_set(image)
if use_mapping:
ma_wrap.diffuse_mapping_set(**tex_map_kw)
elif lnk_type == b'SpecularColor':
ma_wrap.specular_image_set(image)
if use_mapping:
ma_wrap.specular_mapping_set(**tex_map_kw)
elif lnk_type == b'ReflectionColor':
ma_wrap.reflect_image_set(image)
if use_mapping:
ma_wrap.reflect_mapping_set(**tex_map_kw)
Campbell Barton
committed
elif lnk_type == b'TransparentColor': # alpha
ma_wrap.alpha_image_set(image)
if use_mapping:
ma_wrap.alpha_mapping_set(**tex_map_kw)
Campbell Barton
committed
if use_alpha_decals:
material_decals.add(material)
elif lnk_type == b'DiffuseFactor':
pass # TODO
elif lnk_type == b'ShininessExponent':
ma_wrap.hardness_image_set(image)
if use_mapping:
ma_wrap.hardness_mapping_set(**tex_map_kw)
elif lnk_type == b'NormalMap' or lnk_type == b'Bump': # XXX, applications abuse bump!
ma_wrap.normal_image_set(image)
ma_wrap.normal_factor_set(texture_bumpfac_get(fbx_obj))
if use_mapping:
ma_wrap.normal_mapping_set(**tex_map_kw)
"""
elif lnk_type == b'Bump':
ma_wrap.bump_image_set(image)
ma_wrap.bump_factor_set(texture_bumpfac_get(fbx_obj))
if use_mapping:
ma_wrap.bump_mapping_set(**tex_map_kw)
"""
Campbell Barton
committed
else:
print("WARNING: material link %r ignored" % lnk_type)
material_images.setdefault(material, {})[lnk_type] = (image, tex_map)
else:
if fbx_lnk_type.props[0] == b'OP':
lnk_type = fbx_lnk_type.props[3]
# tx/rot/scale (rot is ignored here!).
tex_map = texture_mapping_get(fbx_lnk)
mtex = material_mtex_new(material, image, tex_map)
if lnk_type == b'DiffuseColor':
mtex.use_map_color_diffuse = True
mtex.blend_type = 'MULTIPLY'
elif lnk_type == b'SpecularColor':
mtex.use_map_color_spec = True
mtex.blend_type = 'MULTIPLY'
elif lnk_type == b'ReflectionColor':
mtex.use_map_raymir = True
Campbell Barton
committed
elif lnk_type == b'TransparentColor': # alpha
material.use_transparency = True
material.transparency_method = 'RAYTRACE'
material.alpha = 0.0
mtex.use_map_alpha = True
mtex.alpha_factor = 1.0
if use_alpha_decals:
material_decals.add(material)
elif lnk_type == b'DiffuseFactor':
mtex.use_map_diffuse = True
elif lnk_type == b'ShininessExponent':
mtex.use_map_hardness = True
elif lnk_type == b'NormalMap' or lnk_type == b'Bump': # XXX, applications abuse bump!
mtex.texture.use_normal_map = True # not ideal!
mtex.use_map_normal = True
mtex.normal_factor = texture_bumpfac_get(fbx_obj)
elif lnk_type == b'Bump':
mtex.use_map_normal = True
mtex.normal_factor = texture_bumpfac_get(fbx_obj)
else:
print("WARNING: material link %r ignored" % lnk_type)
Campbell Barton
committed
material_images.setdefault(material, {})[lnk_type] = (image, tex_map)
Campbell Barton
committed
# Check if the diffuse image has an alpha channel,
# if so, use the alpha channel.
# Note: this could be made optional since images may have alpha but be entirely opaque
for fbx_uuid, fbx_item in fbx_table_nodes.items():
fbx_obj, blen_data = fbx_item
if fbx_obj.id != b'Material':
continue
material = fbx_table_nodes.get(fbx_uuid, (None, None))[1]
image, tex_map = material_images.get(material, {}).get(b'DiffuseColor', (None, None))
Campbell Barton
committed
# do we have alpha?
if image and image.depth == 32:
if use_alpha_decals:
material_decals.add(material)
Campbell Barton
committed
if use_cycles:
ma_wrap = cycles_material_wrap_map[material]
if ma_wrap.node_bsdf_alpha.mute:
ma_wrap.alpha_image_set_from_diffuse()
else:
if not any((True for mtex in material.texture_slots if mtex and mtex.use_map_alpha)):
mtex = material_mtex_new(material, image, tex_map)
Campbell Barton
committed
material.use_transparency = True
material.transparency_method = 'RAYTRACE'
material.alpha = 0.0
mtex.use_map_alpha = True
mtex.alpha_factor = 1.0
# propagate mapping from diffuse to all other channels which have none defined.
if use_cycles:
ma_wrap = cycles_material_wrap_map[material]
ma_wrap.mapping_set_from_diffuse()
_(); del _
Campbell Barton
committed
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
def _():
# Annoying workaround for cycles having no z-offset
if material_decals and use_alpha_decals:
for fbx_uuid, fbx_item in fbx_table_nodes.items():
fbx_obj, blen_data = fbx_item
if fbx_obj.id != b'Geometry':
continue
if fbx_obj.props[-1] == b'Mesh':
mesh = fbx_item[1]
if decal_offset != 0.0:
for material in mesh.materials:
if material in material_decals:
for v in mesh.vertices:
v.co += v.normal * decal_offset
break
if use_cycles:
for obj in (obj for obj in bpy.data.objects if obj.data == mesh):
obj.cycles_visibility.shadow = False
else:
for material in mesh.materials:
if material in material_decals:
# recieve but dont cast shadows
material.use_raytrace = False
_(); del _
print('Import finished in %.4f sec.' % (time.process_time() - start_time))
return {'FINISHED'}