Changeset View
Changeset View
Standalone View
Standalone View
io_scene_fbx/import_fbx.py
| Show First 20 Lines • Show All 350 Lines • ▼ Show 20 Lines | if fbx_obj_props: | ||||
| elif prop_type in {b'Enum', b'enum'}: | elif prop_type in {b'Enum', b'enum'}: | ||||
| assert(fbx_prop.props_type[4:6] == bytes((data_types.INT32, data_types.STRING))) | assert(fbx_prop.props_type[4:6] == bytes((data_types.INT32, data_types.STRING))) | ||||
| val = fbx_prop.props[4] | val = fbx_prop.props[4] | ||||
| if settings.use_custom_props_enum_as_string and fbx_prop.props[5]: | if settings.use_custom_props_enum_as_string and fbx_prop.props[5]: | ||||
| enum_items = fbx_prop.props[5].decode('utf-8', 'replace').split('~') | enum_items = fbx_prop.props[5].decode('utf-8', 'replace').split('~') | ||||
| if val >= 0 and val < len(enum_items): | if val >= 0 and val < len(enum_items): | ||||
| blen_obj[prop_name] = enum_items[val] | blen_obj[prop_name] = enum_items[val] | ||||
| else: | else: | ||||
| print ("WARNING: User property '%s' has wrong enum value, skipped" % prop_name) | print ("WARNING: User property '%s' has wrong enum value, skipping." % prop_name) | ||||
| else: | else: | ||||
| blen_obj[prop_name] = val | blen_obj[prop_name] = val | ||||
| else: | else: | ||||
| print ("WARNING: User property type '%s' is not supported" % prop_type.decode('utf-8', 'replace')) | print ("WARNING: User property type '%s' is not supported." | ||||
| "" % prop_type.decode('utf-8', 'replace')) | |||||
| def blen_read_object_transform_do(transform_data): | def blen_read_object_transform_do(transform_data): | ||||
| # This is a nightmare. FBX SDK uses Maya way to compute the transformation matrix of a node - utterly simple: | # This is a nightmare. FBX SDK uses Maya way to compute the transformation matrix of a node - utterly simple: | ||||
| # | # | ||||
| # WorldTransform = ParentWorldTransform @ T @ Roff @ Rp @ Rpre @ R @ Rpost @ Rp-1 @ Soff @ Sp @ S @ Sp-1 | # WorldTransform = ParentWorldTransform @ T @ Roff @ Rp @ Rpre @ R @ Rpost @ Rp-1 @ Soff @ Sp @ S @ Sp-1 | ||||
| # | # | ||||
| # Where all those terms are 4 x 4 matrices that contain: | # Where all those terms are 4 x 4 matrices that contain: | ||||
| ▲ Show 20 Lines • Show All 412 Lines • ▼ Show 20 Lines | def blen_read_geom_array_setattr(generator, blen_data, blen_attr, fbx_data, stride, item_size, descr, xform): | ||||
| print_error = True | print_error = True | ||||
| def check_skip(blen_idx, fbx_idx): | def check_skip(blen_idx, fbx_idx): | ||||
| nonlocal print_error | nonlocal print_error | ||||
| if fbx_idx < 0: # Negative values mean 'skip'. | if fbx_idx < 0: # Negative values mean 'skip'. | ||||
| return True | return True | ||||
| if blen_idx > max_idx: | if blen_idx > max_idx: | ||||
| if print_error: | if print_error: | ||||
| print("ERROR: too much data in this layer, compared to elements in mesh, skipping!") | print("ERROR: Layer contains too much data for the number of elements in the mesh, skipping.") | ||||
| print_error = False | print_error = False | ||||
| return True | return True | ||||
| return False | return False | ||||
| if xform is not None: | if xform is not None: | ||||
| if isinstance(blen_data, list): | if isinstance(blen_data, list): | ||||
| if item_size == 1: | if item_size == 1: | ||||
| def _process(blend_data, blen_attr, fbx_data, xform, item_size, blen_idx, fbx_idx): | def _process(blend_data, blen_attr, fbx_data, xform, item_size, blen_idx, fbx_idx): | ||||
| ▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | for p in mesh.polygons: | ||||
| vidx = loops[lidx].vertex_index | vidx = loops[lidx].vertex_index | ||||
| if vidx < fbx_data_len: | if vidx < fbx_data_len: | ||||
| yield lidx, vidx * stride | yield lidx, vidx * stride | ||||
| # generic error printers. | # generic error printers. | ||||
| def blen_read_geom_array_error_mapping(descr, fbx_layer_mapping, quiet=False): | def blen_read_geom_array_error_mapping(descr, fbx_layer_mapping, quiet=False): | ||||
| if not quiet: | if not quiet: | ||||
| print("warning layer %r mapping type unsupported: %r" % (descr, fbx_layer_mapping)) | print("WARNING: Layer %r mapping type %r is not supported." % (descr, fbx_layer_mapping)) | ||||
| def blen_read_geom_array_error_ref(descr, fbx_layer_ref, quiet=False): | def blen_read_geom_array_error_ref(descr, fbx_layer_ref, quiet=False): | ||||
| if not quiet: | if not quiet: | ||||
| print("warning layer %r ref type unsupported: %r" % (descr, fbx_layer_ref)) | print("WARNING: Layer %r ref type %r is not supported." % (descr, fbx_layer_ref)) | ||||
| def blen_read_geom_array_mapped_vert( | def blen_read_geom_array_mapped_vert( | ||||
| mesh, blen_data, blen_attr, | mesh, blen_data, blen_attr, | ||||
| fbx_layer_data, fbx_layer_index, | fbx_layer_data, fbx_layer_index, | ||||
| fbx_layer_mapping, fbx_layer_ref, | fbx_layer_mapping, fbx_layer_ref, | ||||
| stride, item_size, descr, | stride, item_size, descr, | ||||
| xform=None, quiet=False, | xform=None, quiet=False, | ||||
| ▲ Show 20 Lines • Show All 143 Lines • ▼ Show 20 Lines | def blen_read_geom_layer_material(fbx_obj, mesh): | ||||
| blen_read_geom_array_mapped_polygon( | blen_read_geom_array_mapped_polygon( | ||||
| mesh, blen_data, "material_index", | mesh, blen_data, "material_index", | ||||
| fbx_layer_data, None, | fbx_layer_data, None, | ||||
| fbx_layer_mapping, fbx_layer_ref, | fbx_layer_mapping, fbx_layer_ref, | ||||
| 1, 1, layer_id, | 1, 1, layer_id, | ||||
| ) | ) | ||||
| def blen_read_geom_layer_uv(fbx_obj, mesh): | def blen_read_geom_layer_uv(fbx_obj, mesh, use_duplicate_layers): | ||||
| for layer_id in (b'LayerElementUV',): | import array | ||||
| max_layers = 8 # Blender only allows for 8 UV layers. | |||||
| layer_id = b'LayerElementUV' | |||||
| loaded_uv_data = {} | |||||
| mesh_loop_length = len(mesh.loops) | |||||
| for fbx_layer in elem_find_iter(fbx_obj, layer_id): | for fbx_layer in elem_find_iter(fbx_obj, layer_id): | ||||
| # all should be valid | # all should be valid | ||||
| (fbx_layer_name, | (fbx_layer_name, | ||||
| fbx_layer_mapping, | fbx_layer_mapping, | ||||
| fbx_layer_ref, | fbx_layer_ref, | ||||
| ) = blen_read_geom_layerinfo(fbx_layer) | ) = blen_read_geom_layerinfo(fbx_layer) | ||||
| # Check layer limit | |||||
| if len(loaded_uv_data) >= max_layers: | |||||
| print("WARNING: Blender does not support more than %d UV Maps." % max_layers) | |||||
| print(" Failed to add {%r %r} UV layer to %r." % (layer_id, fbx_layer_name, mesh.name)) | |||||
| continue | |||||
| fbx_layer_data = elem_prop_first(elem_find_first(fbx_layer, b'UV')) | fbx_layer_data = elem_prop_first(elem_find_first(fbx_layer, b'UV')) | ||||
| fbx_layer_index = elem_prop_first(elem_find_first(fbx_layer, b'UVIndex')) | fbx_layer_index = elem_prop_first(elem_find_first(fbx_layer, b'UVIndex')) | ||||
| # Always init our new layers with (0, 0) UVs. | # Initialize new layers with (0, 0) UVs. | ||||
| uv_lay = mesh.uv_layers.new(name=fbx_layer_name, do_init=False) | uv_lay = mesh.uv_layers.new(name=fbx_layer_name, do_init=False) | ||||
| if uv_lay is None: | if uv_lay is None: | ||||
| print("Failed to add {%r %r} UVLayer to %r (probably too many of them?)" | print("ERROR: Failed to add {%r %r} UV layer to %r." % (layer_id, fbx_layer_name, mesh.name)) | ||||
| "" % (layer_id, fbx_layer_name, mesh.name)) | |||||
| continue | continue | ||||
| blen_data = uv_lay.data | blen_data = uv_lay.data | ||||
| # some valid files omit this data | # some valid files omit this data | ||||
| if fbx_layer_data is None: | if fbx_layer_data is None: | ||||
| print("%r %r missing data" % (layer_id, fbx_layer_name)) | print("WARNING: {%r %r} is missing data." % (layer_id, fbx_layer_name)) | ||||
| continue | else: | ||||
| blen_read_geom_array_mapped_polyloop( | blen_read_geom_array_mapped_polyloop( | ||||
| mesh, blen_data, "uv", | mesh, blen_data, "uv", | ||||
| fbx_layer_data, fbx_layer_index, | fbx_layer_data, fbx_layer_index, | ||||
| fbx_layer_mapping, fbx_layer_ref, | fbx_layer_mapping, fbx_layer_ref, | ||||
| 2, 2, layer_id, | 2, 2, layer_id, | ||||
| ) | ) | ||||
| if use_duplicate_layers: | |||||
| loaded_uv_data[fbx_layer_name] = True | |||||
| continue | |||||
| # Check existing UV maps and remove the new one if it is a duplicate. | |||||
| duplicate_of = None | |||||
| uvs = array.array('f', [0] * (2 * mesh_loop_length)) | |||||
| for i in range(mesh_loop_length): | |||||
| uv = getattr(blen_data[i], "uv").to_tuple(4) | |||||
| uvs[i * 2] = uv[0] | |||||
| uvs[i * 2 + 1] = uv[1] | |||||
| for existing_name, existing_uvs in loaded_uv_data.items(): | |||||
| if existing_uvs == uvs: | |||||
| duplicate_of = existing_name | |||||
| break | |||||
| if duplicate_of is not None: | |||||
| print("Removing {%r %r} from %r since it is a duplicate of %r." | |||||
| "" % (layer_id, fbx_layer_name, mesh.name, duplicate_of)) | |||||
| mesh.uv_layers.remove(uv_lay) | |||||
| else: | |||||
| loaded_uv_data[fbx_layer_name] = uvs | |||||
| def blen_read_geom_layer_color(fbx_obj, mesh): | |||||
| # almost same as UV's | def blen_read_geom_layer_color(fbx_obj, mesh, use_duplicate_layers): | ||||
| for layer_id in (b'LayerElementColor',): | import array | ||||
| # Color layers are handled in a similar way to the UVs. | |||||
| layer_id = b'LayerElementColor' | |||||
| loaded_color_data = {} | |||||
| mesh_loop_length = len(mesh.loops) | |||||
| for fbx_layer in elem_find_iter(fbx_obj, layer_id): | for fbx_layer in elem_find_iter(fbx_obj, layer_id): | ||||
| # all should be valid | # all should be valid | ||||
| (fbx_layer_name, | (fbx_layer_name, | ||||
| fbx_layer_mapping, | fbx_layer_mapping, | ||||
| fbx_layer_ref, | fbx_layer_ref, | ||||
| ) = blen_read_geom_layerinfo(fbx_layer) | ) = blen_read_geom_layerinfo(fbx_layer) | ||||
| fbx_layer_data = elem_prop_first(elem_find_first(fbx_layer, b'Colors')) | fbx_layer_data = elem_prop_first(elem_find_first(fbx_layer, b'Colors')) | ||||
| fbx_layer_index = elem_prop_first(elem_find_first(fbx_layer, b'ColorIndex')) | fbx_layer_index = elem_prop_first(elem_find_first(fbx_layer, b'ColorIndex')) | ||||
| # Always init our new layers with full white opaque color. | # Initialize new color layers to be fully opaque white. | ||||
| color_lay = mesh.vertex_colors.new(name=fbx_layer_name, do_init=False) | color_lay = mesh.vertex_colors.new(name=fbx_layer_name, do_init=False) | ||||
| if color_lay is None: | if color_lay is None: | ||||
| print("Failed to add {%r %r} vertex color layer to %r (probably too many of them?)" | print("WARNING: Failed to add {%r %r} color attribute layer to %r." | ||||
| "" % (layer_id, fbx_layer_name, mesh.name)) | "" % (layer_id, fbx_layer_name, mesh.name)) | ||||
| continue | continue | ||||
| blen_data = color_lay.data | blen_data = color_lay.data | ||||
| # some valid files omit this data | # some valid files omit this data | ||||
| if fbx_layer_data is None: | if fbx_layer_data is None: | ||||
| print("%r %r missing data" % (layer_id, fbx_layer_name)) | print("WARNING: {%r %r} is missing data." % (layer_id, fbx_layer_name)) | ||||
| continue | else: | ||||
| blen_read_geom_array_mapped_polyloop( | blen_read_geom_array_mapped_polyloop( | ||||
| mesh, blen_data, "color", | mesh, blen_data, "color", | ||||
| fbx_layer_data, fbx_layer_index, | fbx_layer_data, fbx_layer_index, | ||||
| fbx_layer_mapping, fbx_layer_ref, | fbx_layer_mapping, fbx_layer_ref, | ||||
| 4, 4, layer_id, | 4, 4, layer_id, | ||||
| ) | ) | ||||
| if use_duplicate_layers: | |||||
| loaded_color_data[fbx_layer_name] = True | |||||
| continue | |||||
| # Check existing vertex color layers and remove the new one if it is a duplicate. | |||||
| duplicate_of = None | |||||
| colors = array.array('f', [0] * (4 * mesh_loop_length)) | |||||
| for i in range(mesh_loop_length): | |||||
| color = Vector(getattr(blen_data[i], "color")).to_tuple(4) | |||||
| colors[i * 4] = color[0] | |||||
| colors[i * 4 + 1] = color[1] | |||||
| colors[i * 4 + 2] = color[2] | |||||
| colors[i * 4 + 3] = color[3] | |||||
| for existing_name, existing_colors in loaded_color_data.items(): | |||||
| if existing_colors == colors: | |||||
| duplicate_of = existing_name | |||||
| break | |||||
| if duplicate_of is not None: | |||||
| print("Removing {%r %r} from %r since it is a duplicate of %r." | |||||
| "" % (layer_id, fbx_layer_name, mesh.name, duplicate_of)) | |||||
| mesh.vertex_colors.remove(color_lay) | |||||
| else: | |||||
| loaded_color_data[fbx_layer_name] = colors | |||||
| def blen_read_geom_layer_smooth(fbx_obj, mesh): | def blen_read_geom_layer_smooth(fbx_obj, mesh): | ||||
| fbx_layer = elem_find_first(fbx_obj, b'LayerElementSmoothing') | fbx_layer = elem_find_first(fbx_obj, b'LayerElementSmoothing') | ||||
| if fbx_layer is None: | if fbx_layer is None: | ||||
| return False | return False | ||||
| # all should be valid | # all should be valid | ||||
| (fbx_layer_name, | (fbx_layer_name, | ||||
| fbx_layer_mapping, | fbx_layer_mapping, | ||||
| fbx_layer_ref, | fbx_layer_ref, | ||||
| ) = blen_read_geom_layerinfo(fbx_layer) | ) = blen_read_geom_layerinfo(fbx_layer) | ||||
| layer_id = b'Smoothing' | layer_id = b'Smoothing' | ||||
| fbx_layer_data = elem_prop_first(elem_find_first(fbx_layer, layer_id)) | fbx_layer_data = elem_prop_first(elem_find_first(fbx_layer, layer_id)) | ||||
| # udk has 'Direct' mapped, with no Smoothing, not sure why, but ignore these | # udk has 'Direct' mapped, with no Smoothing, not sure why, but ignore these | ||||
| if fbx_layer_data is None: | if fbx_layer_data is None: | ||||
| return False | return False | ||||
| if fbx_layer_mapping == b'ByEdge': | if fbx_layer_mapping == b'ByEdge': | ||||
| # some models have bad edge data, we can't use this info... | # some models have bad edge data, we can't use this info... | ||||
| if not mesh.edges: | if not mesh.edges: | ||||
| print("warning skipping sharp edges data, no valid edges...") | print("WARNING: Skipping sharp edges data, no valid edges...") | ||||
| return False | return False | ||||
| blen_data = mesh.edges | blen_data = mesh.edges | ||||
| blen_read_geom_array_mapped_edge( | blen_read_geom_array_mapped_edge( | ||||
| mesh, blen_data, "use_edge_sharp", | mesh, blen_data, "use_edge_sharp", | ||||
| fbx_layer_data, None, | fbx_layer_data, None, | ||||
| fbx_layer_mapping, fbx_layer_ref, | fbx_layer_mapping, fbx_layer_ref, | ||||
| 1, 1, layer_id, | 1, 1, layer_id, | ||||
| xform=lambda s: not s, | xform=lambda s: not s, | ||||
| ) | ) | ||||
| # We only set sharp edges here, not face smoothing itself... | # We only set sharp edges here, not face smoothing itself... | ||||
| mesh.use_auto_smooth = True | mesh.use_auto_smooth = True | ||||
| return False | return False | ||||
| elif fbx_layer_mapping == b'ByPolygon': | elif fbx_layer_mapping == b'ByPolygon': | ||||
| blen_data = mesh.polygons | blen_data = mesh.polygons | ||||
| return blen_read_geom_array_mapped_polygon( | return blen_read_geom_array_mapped_polygon( | ||||
| mesh, blen_data, "use_smooth", | mesh, blen_data, "use_smooth", | ||||
| fbx_layer_data, None, | fbx_layer_data, None, | ||||
| fbx_layer_mapping, fbx_layer_ref, | fbx_layer_mapping, fbx_layer_ref, | ||||
| 1, 1, layer_id, | 1, 1, layer_id, | ||||
| xform=lambda s: (s != 0), # smoothgroup bitflags, treat as booleans for now | xform=lambda s: (s != 0), # smoothgroup bitflags, treat as booleans for now | ||||
| ) | ) | ||||
| else: | else: | ||||
| print("warning layer %r mapping type unsupported: %r" % (fbx_layer.id, fbx_layer_mapping)) | print("WARNING: Layer %r mapping type %r is not supported." % (fbx_layer.id, fbx_layer_mapping)) | ||||
| return False | return False | ||||
| def blen_read_geom_layer_edge_crease(fbx_obj, mesh): | def blen_read_geom_layer_edge_crease(fbx_obj, mesh): | ||||
| from math import sqrt | from math import sqrt | ||||
| fbx_layer = elem_find_first(fbx_obj, b'LayerElementEdgeCrease') | fbx_layer = elem_find_first(fbx_obj, b'LayerElementEdgeCrease') | ||||
| if fbx_layer is None: | if fbx_layer is None: | ||||
| return False | return False | ||||
| # all should be valid | # all should be valid | ||||
| (fbx_layer_name, | (fbx_layer_name, | ||||
| fbx_layer_mapping, | fbx_layer_mapping, | ||||
| fbx_layer_ref, | fbx_layer_ref, | ||||
| ) = blen_read_geom_layerinfo(fbx_layer) | ) = blen_read_geom_layerinfo(fbx_layer) | ||||
| if fbx_layer_mapping != b'ByEdge': | if fbx_layer_mapping != b'ByEdge': | ||||
| return False | return False | ||||
| layer_id = b'EdgeCrease' | layer_id = b'EdgeCrease' | ||||
| fbx_layer_data = elem_prop_first(elem_find_first(fbx_layer, layer_id)) | fbx_layer_data = elem_prop_first(elem_find_first(fbx_layer, layer_id)) | ||||
| # some models have bad edge data, we can't use this info... | # some models have bad edge data, we can't use this info... | ||||
| if not mesh.edges: | if not mesh.edges: | ||||
| print("warning skipping edge crease data, no valid edges...") | print("WARNING: Skipping edge crease data, no valid edges...") | ||||
| return False | return False | ||||
| if fbx_layer_mapping == b'ByEdge': | if fbx_layer_mapping == b'ByEdge': | ||||
| # some models have bad edge data, we can't use this info... | # some models have bad edge data, we can't use this info... | ||||
| if not mesh.edges: | if not mesh.edges: | ||||
| print("warning skipping edge crease data, no valid edges...") | print("WARNING: Skipping edge crease data, no valid edges...") | ||||
| return False | return False | ||||
| blen_data = mesh.edges | blen_data = mesh.edges | ||||
| return blen_read_geom_array_mapped_edge( | return blen_read_geom_array_mapped_edge( | ||||
| mesh, blen_data, "crease", | mesh, blen_data, "crease", | ||||
| fbx_layer_data, None, | fbx_layer_data, None, | ||||
| fbx_layer_mapping, fbx_layer_ref, | fbx_layer_mapping, fbx_layer_ref, | ||||
| 1, 1, layer_id, | 1, 1, layer_id, | ||||
| # Blender squares those values before sending them to OpenSubdiv, when other software don't, | # Blender squares those values before sending them to OpenSubdiv, when other software don't, | ||||
| # so we need to compensate that to get similar results through FBX... | # so we need to compensate that to get similar results through FBX... | ||||
| xform=sqrt, | xform=sqrt, | ||||
| ) | ) | ||||
| else: | else: | ||||
| print("warning layer %r mapping type unsupported: %r" % (fbx_layer.id, fbx_layer_mapping)) | print("WARNING: Layer %r mapping type %r is not supported." % (fbx_layer.id, fbx_layer_mapping)) | ||||
| return False | return False | ||||
| def blen_read_geom_layer_normal(fbx_obj, mesh, xform=None): | def blen_read_geom_layer_normal(fbx_obj, mesh, xform=None): | ||||
| fbx_layer = elem_find_first(fbx_obj, b'LayerElementNormal') | fbx_layer = elem_find_first(fbx_obj, b'LayerElementNormal') | ||||
| if fbx_layer is None: | if fbx_layer is None: | ||||
| return False | return False | ||||
| (fbx_layer_name, | (fbx_layer_name, | ||||
| fbx_layer_mapping, | fbx_layer_mapping, | ||||
| fbx_layer_ref, | fbx_layer_ref, | ||||
| ) = blen_read_geom_layerinfo(fbx_layer) | ) = blen_read_geom_layerinfo(fbx_layer) | ||||
| layer_id = b'Normals' | layer_id = b'Normals' | ||||
| fbx_layer_data = elem_prop_first(elem_find_first(fbx_layer, layer_id)) | fbx_layer_data = elem_prop_first(elem_find_first(fbx_layer, layer_id)) | ||||
| fbx_layer_index = elem_prop_first(elem_find_first(fbx_layer, b'NormalsIndex')) | fbx_layer_index = elem_prop_first(elem_find_first(fbx_layer, b'NormalsIndex')) | ||||
| if fbx_layer_data is None: | if fbx_layer_data is None: | ||||
| print("warning %r %r missing data" % (layer_id, fbx_layer_name)) | print("WARNING: %r %r is missing data." % (layer_id, fbx_layer_name)) | ||||
| return False | return False | ||||
| # try loops, then vertices. | # try loops, then vertices. | ||||
| tries = ((mesh.loops, "Loops", False, blen_read_geom_array_mapped_polyloop), | tries = ((mesh.loops, "Loops", False, blen_read_geom_array_mapped_polyloop), | ||||
| (mesh.polygons, "Polygons", True, blen_read_geom_array_mapped_polygon), | (mesh.polygons, "Polygons", True, blen_read_geom_array_mapped_polygon), | ||||
| (mesh.vertices, "Vertices", True, blen_read_geom_array_mapped_vert)) | (mesh.vertices, "Vertices", True, blen_read_geom_array_mapped_vert)) | ||||
| for blen_data, blen_data_type, is_fake, func in tries: | for blen_data, blen_data_type, is_fake, func in tries: | ||||
| bdata = [None] * len(blen_data) if is_fake else blen_data | bdata = [None] * len(blen_data) if is_fake else blen_data | ||||
| ▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | if fbx_polys: | ||||
| index ^= -1 | index ^= -1 | ||||
| l.vertex_index = index | l.vertex_index = index | ||||
| mesh.polygons.add(len(poly_loop_starts)) | mesh.polygons.add(len(poly_loop_starts)) | ||||
| mesh.polygons.foreach_set("loop_start", poly_loop_starts) | mesh.polygons.foreach_set("loop_start", poly_loop_starts) | ||||
| mesh.polygons.foreach_set("loop_total", poly_loop_totals) | mesh.polygons.foreach_set("loop_total", poly_loop_totals) | ||||
| blen_read_geom_layer_material(fbx_obj, mesh) | blen_read_geom_layer_material(fbx_obj, mesh) | ||||
| blen_read_geom_layer_uv(fbx_obj, mesh) | |||||
| blen_read_geom_layer_color(fbx_obj, mesh) | use_duplicate_layers = settings.use_duplicate_layers | ||||
| blen_read_geom_layer_uv(fbx_obj, mesh, use_duplicate_layers) | |||||
| blen_read_geom_layer_color(fbx_obj, mesh, use_duplicate_layers) | |||||
| if fbx_edges: | if fbx_edges: | ||||
| # edges in fact index the polygons (NOT the vertices) | # edges in fact index the polygons (NOT the vertices) | ||||
| import array | import array | ||||
| tot_edges = len(fbx_edges) | tot_edges = len(fbx_edges) | ||||
| edges_conv = array.array('i', [0]) * (tot_edges * 2) | edges_conv = array.array('i', [0]) * (tot_edges * 2) | ||||
| edge_index = 0 | edge_index = 0 | ||||
| ▲ Show 20 Lines • Show All 199 Lines • ▼ Show 20 Lines | if filepath: | ||||
| # Make sure we do handle a relative path, and not an absolute one (see D5143). | # Make sure we do handle a relative path, and not an absolute one (see D5143). | ||||
| filepath = filepath.lstrip(os.path.sep).lstrip(os.path.altsep) | filepath = filepath.lstrip(os.path.sep).lstrip(os.path.altsep) | ||||
| filepath = os.path.join(basedir, filepath) | filepath = os.path.join(basedir, filepath) | ||||
| else: | else: | ||||
| filepath = elem_find_first_string(fbx_obj, b'FileName') | filepath = elem_find_first_string(fbx_obj, b'FileName') | ||||
| if not filepath: | if not filepath: | ||||
| filepath = elem_find_first_string(fbx_obj, b'Filename') | filepath = elem_find_first_string(fbx_obj, b'Filename') | ||||
| if not filepath: | if not filepath: | ||||
| print("Error, could not find any file path in ", fbx_obj) | print("ERROR: Could not find any file path in ", fbx_obj) | ||||
| print(" Falling back to: ", elem_name_utf8) | print(" Falling back to: ", elem_name_utf8) | ||||
| filepath = elem_name_utf8 | filepath = elem_name_utf8 | ||||
| else : | else: | ||||
| filepath = filepath.replace('\\', '/') if (os.sep == '/') else filepath.replace('/', '\\') | filepath = filepath.replace('\\', '/') if (os.sep == '/') else filepath.replace('/', '\\') | ||||
| image = image_cache.get(filepath) | image = image_cache.get(filepath) | ||||
| if image is not None: | if image is not None: | ||||
| # Data is only embedded once, we may have already created the image but still be missing its data! | # Data is only embedded once, we may have already created the image but still be missing its data! | ||||
| if not image.has_data: | if not image.has_data: | ||||
| pack_data_from_content(image, fbx_obj) | pack_data_from_content(image, fbx_obj) | ||||
| return image | return image | ||||
| ▲ Show 20 Lines • Show All 827 Lines • ▼ Show 20 Lines | |||||
| def load(operator, context, filepath="", | def load(operator, context, filepath="", | ||||
| use_manual_orientation=False, | use_manual_orientation=False, | ||||
| axis_forward='-Z', | axis_forward='-Z', | ||||
| axis_up='Y', | axis_up='Y', | ||||
| global_scale=1.0, | global_scale=1.0, | ||||
| bake_space_transform=False, | bake_space_transform=False, | ||||
| use_custom_normals=True, | use_custom_normals=True, | ||||
| use_duplicate_layers=True, | |||||
| use_image_search=False, | use_image_search=False, | ||||
| use_alpha_decals=False, | use_alpha_decals=False, | ||||
| decal_offset=0.0, | decal_offset=0.0, | ||||
| use_anim=True, | use_anim=True, | ||||
| anim_offset=1.0, | anim_offset=1.0, | ||||
| use_subsurf=False, | use_subsurf=False, | ||||
| use_custom_props=True, | use_custom_props=True, | ||||
| use_custom_props_enum_as_string=True, | use_custom_props_enum_as_string=True, | ||||
| ▲ Show 20 Lines • Show All 129 Lines • ▼ Show 20 Lines | if real_fps <= 0.0: | ||||
| real_fps = 25.0 | real_fps = 25.0 | ||||
| scene.render.fps = round(real_fps) | scene.render.fps = round(real_fps) | ||||
| scene.render.fps_base = scene.render.fps / real_fps | scene.render.fps_base = scene.render.fps / real_fps | ||||
| # store global settings that need to be accessed during conversion | # store global settings that need to be accessed during conversion | ||||
| settings = FBXImportSettings( | settings = FBXImportSettings( | ||||
| operator.report, (axis_up, axis_forward), global_matrix, global_scale, | operator.report, (axis_up, axis_forward), global_matrix, global_scale, | ||||
| bake_space_transform, global_matrix_inv, global_matrix_inv_transposed, | bake_space_transform, global_matrix_inv, global_matrix_inv_transposed, | ||||
| use_custom_normals, use_image_search, | use_custom_normals, use_duplicate_layers, use_image_search, | ||||
| use_alpha_decals, decal_offset, | use_alpha_decals, decal_offset, | ||||
| use_anim, anim_offset, | use_anim, anim_offset, | ||||
| use_subsurf, | use_subsurf, | ||||
| use_custom_props, use_custom_props_enum_as_string, | use_custom_props, use_custom_props_enum_as_string, | ||||
| nodal_material_wrap_map, image_cache, | nodal_material_wrap_map, image_cache, | ||||
| ignore_leaf_bones, force_connect_children, automatic_bone_orientation, bone_correction_matrix, | ignore_leaf_bones, force_connect_children, automatic_bone_orientation, bone_correction_matrix, | ||||
| use_prepost_rot, | use_prepost_rot, | ||||
| ) | ) | ||||
| ▲ Show 20 Lines • Show All 501 Lines • ▼ Show 20 Lines | if use_anim: | ||||
| items.append((cam, lnk_prop)) | items.append((cam, lnk_prop)) | ||||
| elif lnk_prop == b'DiffuseColor': | elif lnk_prop == b'DiffuseColor': | ||||
| from bpy.types import Material | from bpy.types import Material | ||||
| fbx_item = fbx_table_nodes.get(n_uuid, None) | fbx_item = fbx_table_nodes.get(n_uuid, None) | ||||
| if fbx_item is None or not isinstance(fbx_item[1], Material): | if fbx_item is None or not isinstance(fbx_item[1], Material): | ||||
| continue | continue | ||||
| mat = fbx_item[1] | mat = fbx_item[1] | ||||
| items.append((mat, lnk_prop)) | items.append((mat, lnk_prop)) | ||||
| print("WARNING! Importing material's animation is not supported for Nodal materials...") | print("WARNING: Importing material animation is not supported for nodal materials...") | ||||
| for al_uuid, al_ctype in fbx_connection_map.get(acn_uuid, ()): | for al_uuid, al_ctype in fbx_connection_map.get(acn_uuid, ()): | ||||
| if al_ctype.props[0] != b'OO': | if al_ctype.props[0] != b'OO': | ||||
| continue | continue | ||||
| fbx_aldata, _blen_aldata = fbx_alitem = fbx_table_nodes.get(al_uuid, (None, None)) | 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'': | if fbx_aldata is None or fbx_aldata.id != b'AnimationLayer' or fbx_aldata.props[2] != b'': | ||||
| continue | continue | ||||
| for as_uuid in get_astacks_from_alayer(al_uuid): | for as_uuid in get_astacks_from_alayer(al_uuid): | ||||
| _fbx_alitem, anim_items = stacks[as_uuid][1][al_uuid] | _fbx_alitem, anim_items = stacks[as_uuid][1][al_uuid] | ||||
| ▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | def _(): | ||||
| for (fbx_lnk_material, material, fbx_lnk_material_type) in connection_filter_reverse(fbx_lnk_uuid, b'Material'): | for (fbx_lnk_material, material, fbx_lnk_material_type) in connection_filter_reverse(fbx_lnk_uuid, b'Material'): | ||||
| if material not in done_materials: | if material not in done_materials: | ||||
| mesh.materials.append(material) | mesh.materials.append(material) | ||||
| done_materials.add(material) | done_materials.add(material) | ||||
| # We have to validate mesh polygons' ma_idx, see T41015! | # 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. | # Some FBX seem to have an extra 'default' material which is not defined in FBX file. | ||||
| if mesh.validate_material_indices(): | if mesh.validate_material_indices(): | ||||
| print("WARNING: mesh '%s' had invalid material indices, those were reset to first material" % mesh.name) | print("WARNING: Mesh '%s' had invalid material indices. These indices were reset to first material." | ||||
| "" % mesh.name) | |||||
| _(); del _ | _(); del _ | ||||
| perfmon.step("FBX import: Assign textures...") | perfmon.step("FBX import: Assign textures...") | ||||
| def _(): | def _(): | ||||
| material_images = {} | material_images = {} | ||||
| fbx_tmpl = fbx_template_get((b'Material', b'KFbxSurfacePhong')) | fbx_tmpl = fbx_template_get((b'Material', b'KFbxSurfacePhong')) | ||||
| ▲ Show 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | def _(): | ||||
| """ | """ | ||||
| elif lnk_type in {b'EmissiveColor'}: | elif lnk_type in {b'EmissiveColor'}: | ||||
| ma_wrap.emission_color_texture.image = image | ma_wrap.emission_color_texture.image = image | ||||
| texture_mapping_set(fbx_lnk, ma_wrap.emission_color_texture) | texture_mapping_set(fbx_lnk, ma_wrap.emission_color_texture) | ||||
| elif lnk_type in {b'EmissiveFactor'}: | elif lnk_type in {b'EmissiveFactor'}: | ||||
| ma_wrap.emission_strength_texture.image = image | ma_wrap.emission_strength_texture.image = image | ||||
| texture_mapping_set(fbx_lnk, ma_wrap.emission_strength_texture) | texture_mapping_set(fbx_lnk, ma_wrap.emission_strength_texture) | ||||
| else: | else: | ||||
| print("WARNING: material link %r ignored" % lnk_type) | print("WARNING: Material link %r ignored." % lnk_type) | ||||
| material_images.setdefault(material, {})[lnk_type] = image | material_images.setdefault(material, {})[lnk_type] = image | ||||
| # Check if the diffuse image has an alpha channel, | # Check if the diffuse image has an alpha channel, | ||||
| # if so, use the alpha channel. | # if so, use the alpha channel. | ||||
| # Note: this could be made optional since images may have alpha but be entirely opaque | # 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(): | for fbx_uuid, fbx_item in fbx_table_nodes.items(): | ||||
| ▲ Show 20 Lines • Show All 49 Lines • Show Last 20 Lines | |||||