Skip to content
Snippets Groups Projects
import_fbx.py 136 KiB
Newer Older
  • Learn to ignore specific revisions
  •                         if fbx_item is None or not isinstance(fbx_item[1], Camera):
                                continue
                            cam = fbx_item[1]
                            items.append((cam, lnk_prop))
    
                        elif lnk_prop == b'DiffuseColor':
                            from bpy.types import Material
                            fbx_item = fbx_table_nodes.get(n_uuid, None)
                            if fbx_item is None or not isinstance(fbx_item[1], Material):
                                continue
                            mat = fbx_item[1]
                            items.append((mat, lnk_prop))
    
                            print("WARNING! Importing material's animation is not supported for Nodal materials...")
    
                    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'':
    
                    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,
                            b'd|FocalLength': 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!
                blen_read_animations(fbx_tmpl_astack, fbx_tmpl_alayer, stacks, scene, settings.anim_offset)
    
        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
    
    
                # In Blender, we link materials to data, typically (meshes), while in FBX they are linked to objects...
                # So we have to be careful not to re-add endlessly the same material to a mesh!
                # This can easily happen with 'baked' dupliobjects, see T44386.
                # TODO: add an option to link materials to objects in Blender instead?
    
                done_materials = set()
    
                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'):
    
                        if material not in done_materials:
    
                            done_materials.add(material)
    
                # We have to validate mesh polygons' ma_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)
    
            fbx_tmpl = fbx_template_get((b'Material', b'KFbxSurfacePhong'))
            # b'KFbxSurfaceLambert'
    
    
            def texture_mapping_set(fbx_obj, node_texture):
    
                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))
    
                loc = elem_props_get_vector_3d(fbx_props, b'Translation', (0.0, 0.0, 0.0))
                rot = tuple(-r for r in elem_props_get_vector_3d(fbx_props, b'Rotation', (0.0, 0.0, 0.0)))
                scale = tuple(((1.0 / s) if s != 0.0 else 1.0)
                              for s in elem_props_get_vector_3d(fbx_props, b'Scaling', (1.0, 1.0, 1.0)))
    
                clamp = (bool(elem_props_get_enum(fbx_props, b'WrapModeU', 0)) or
                         bool(elem_props_get_enum(fbx_props, b'WrapModeV', 0)))
    
    
                if (loc == (0.0, 0.0, 0.0) and
                    rot == (0.0, 0.0, 0.0) and
                    scale == (1.0, 1.0, 1.0) and
    
                    return
    
                node_texture.translation = loc
                node_texture.rotation = rot
                node_texture.scale = scale
    
    
            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 fbx_lnk_type.props[0] == b'OP':
                        lnk_type = fbx_lnk_type.props[3]
    
                        ma_wrap = nodal_material_wrap_map[material]
    
    
                        if lnk_type in {b'DiffuseColor', b'3dsMax|maps|texmap_diffuse'}:
    
                            ma_wrap.base_color_texture.image = image
                            texture_mapping_set(fbx_lnk, ma_wrap.base_color_texture)
    
                        elif lnk_type in {b'SpecularColor', b'SpecularFactor'}:
    
                            # Intensity actually, not color...
                            ma_wrap.specular_texture.image = image
                            texture_mapping_set(fbx_lnk, ma_wrap.specular_texture)
    
                        elif lnk_type in {b'ReflectionColor', b'ReflectionFactor', b'3dsMax|maps|texmap_reflection'}:
    
                            # Intensity actually, not color...
                            ma_wrap.metallic_texture.image = image
                            texture_mapping_set(fbx_lnk, ma_wrap.metallic_texture)
    
                        elif lnk_type in {b'TransparentColor', b'TransparentFactor'}:
    
                            ma_wrap.alpha_texture.image = image
                            texture_mapping_set(fbx_lnk, ma_wrap.alpha_texture)
    
                            if use_alpha_decals:
                                material_decals.add(material)
                        elif lnk_type == b'ShininessExponent':
    
                            # That is probably reversed compared to expected results? TODO...
                            ma_wrap.roughness_texture.image = image
                            texture_mapping_set(fbx_lnk, ma_wrap.roughness_texture)
    
                        # XXX, applications abuse bump!
                        elif lnk_type in {b'NormalMap', b'Bump', b'3dsMax|maps|texmap_bump'}:
    
                            ma_wrap.normalmap_texture.image = image
                            texture_mapping_set(fbx_lnk, ma_wrap.normalmap_texture)
    
                            """
                        elif lnk_type == b'Bump':
    
                            # TODO displacement...
    
                        elif lnk_type in {b'EmissiveColor'}:
                            ma_wrap.emission_color_texture.image = image
                            texture_mapping_set(fbx_lnk, ma_wrap.emission_color_texture)
    
                        elif lnk_type in {b'EmissiveFactor'}:
                            ma_wrap.emission_strength_texture.image = image
                            texture_mapping_set(fbx_lnk, ma_wrap.emission_strength_texture)
    
                        else:
                            print("WARNING: material link %r ignored" % lnk_type)
    
                        material_images.setdefault(material, {})[lnk_type] = image
    
    
            # 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 = material_images.get(material, {}).get(b'DiffuseColor', None)
    
                # do we have alpha?
                if image and image.depth == 32:
                    if use_alpha_decals:
                        material_decals.add(material)
    
                    ma_wrap = nodal_material_wrap_map[material]
    
                    ma_wrap.alpha_texture.use_alpha = True
                    ma_wrap.alpha_texture.copy_from(ma_wrap.base_color_texture)
    
                # Propagate mapping from diffuse to all other channels which have none defined.
                # XXX Commenting for now, I do not really understand the logic here, why should diffuse mapping
                #     be applied to all others if not defined for them???
                # ~ ma_wrap = nodal_material_wrap_map[material]
                # ~ ma_wrap.mapping_set_from_diffuse()
    
        perfmon.step("FBX import: Cycles z-offset workaround...")
    
    
        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
    
    
                        for obj in (obj for obj in bpy.data.objects if obj.data == mesh):
    
                            obj.visible_shadow = False