FBX: Add material name collision mode

Introduces the Material Name Collision option, similar to how
USD / OBJ importers have it.

Pull Request: https://projects.blender.org/blender/blender/pulls/144375
This commit is contained in:
Oxicid
2025-08-15 15:04:36 +02:00
committed by Aras Pranckevicius
parent da46eed108
commit b1d9a91647
6 changed files with 74 additions and 7 deletions

View File

@@ -198,6 +198,15 @@ class ImportFBX(bpy.types.Operator, ImportHelper):
description="Use pre/post rotation from FBX transform (you may have to disable that in some cases)",
default=True,
)
mtl_name_collision_mode: EnumProperty(
name="Material Name Collision",
items=(("MAKE_UNIQUE", "Make Unique", "Import each FBX material as a unique Blender material"),
("REFERENCE_EXISTING", "Reference Existing",
"If a material with the same name already exists, reference that instead of importing"),
),
default='MAKE_UNIQUE',
description="Behavior when the name of an imported material conflicts with an existing material",
)
def draw(self, context):
layout = self.layout
@@ -206,6 +215,7 @@ class ImportFBX(bpy.types.Operator, ImportHelper):
import_panel_include(layout, self)
import_panel_transform(layout, self)
import_panel_materials(layout, self)
import_panel_animation(layout, self)
import_panel_armature(layout, self)
@@ -267,6 +277,11 @@ def import_panel_transform_orientation(layout, operator):
body.prop(operator, "axis_forward")
body.prop(operator, "axis_up")
def import_panel_materials(layout, operator):
header, body = layout.panel("FBX_import_material", default_closed=True)
header.label(text="Materials")
if body:
body.prop(operator, "mtl_name_collision_mode")
def import_panel_animation(layout, operator):
header, body = layout.panel("FBX_import_animation", default_closed=True)
@@ -290,7 +305,6 @@ def import_panel_armature(layout, operator):
sub.prop(operator, "primary_bone_axis")
sub.prop(operator, "secondary_bone_axis")
@orientation_helper(axis_forward='-Z', axis_up='Y')
class ExportFBX(bpy.types.Operator, ExportHelper):
"""Write a FBX file"""

View File

@@ -1927,5 +1927,5 @@ FBXImportSettings = namedtuple("FBXImportSettings", (
"use_custom_props", "use_custom_props_enum_as_string",
"nodal_material_wrap_map", "image_cache",
"ignore_leaf_bones", "force_connect_children", "automatic_bone_orientation", "bone_correction_matrix",
"use_prepost_rot", "colors_type",
"use_prepost_rot", "colors_type", "mtl_name_collision_mode",
))

View File

@@ -2050,6 +2050,10 @@ def blen_read_material(fbx_tmpl, fbx_obj, settings):
elem_name_utf8 = elem_name_ensure_class(fbx_obj, b'Material')
if settings.mtl_name_collision_mode == "REFERENCE_EXISTING":
if (ma := bpy.data.materials.get(elem_name_utf8)):
return ma
nodal_material_wrap_map = settings.nodal_material_wrap_map
ma = bpy.data.materials.new(name=elem_name_utf8)
@@ -3043,7 +3047,8 @@ def load(operator, context, filepath="",
primary_bone_axis='Y',
secondary_bone_axis='X',
use_prepost_rot=True,
colors_type='SRGB'):
colors_type='SRGB',
mtl_name_collision_mode="MAKE_UNIQUE"):
global fbx_elem_nil
fbx_elem_nil = FBXElem('', (), (), ())
@@ -3182,7 +3187,7 @@ def load(operator, context, filepath="",
use_custom_props, use_custom_props_enum_as_string,
nodal_material_wrap_map, image_cache,
ignore_leaf_bones, force_connect_children, automatic_bone_orientation, bone_correction_matrix,
use_prepost_rot, colors_type,
use_prepost_rot, colors_type, mtl_name_collision_mode,
)
# #### And now, the "real" data.

View File

@@ -33,6 +33,20 @@
# include "io_fbx_ops.hh"
# include "io_utils.hh"
const EnumPropertyItem rna_enum_fbx_mtl_name_collision_mode_items[] = {
{int(eFBXMtlNameCollisionMode::MakeUnique),
"MAKE_UNIQUE",
0,
"Make Unique",
"Import each FBX material as a unique Blender material"},
{int(eFBXMtlNameCollisionMode::ReferenceExisting),
"REFERENCE_EXISTING",
0,
"Reference Existing",
"If a material with the same name already exists, reference that instead of importing"},
{0, nullptr, 0, nullptr, nullptr},
};
static const EnumPropertyItem fbx_vertex_colors_mode[] = {
{int(eFBXVertexColorMode::None), "NONE", 0, "None", "Do not import color attributes"},
{int(eFBXVertexColorMode::sRGB),
@@ -60,6 +74,8 @@ static wmOperatorStatus wm_fbx_import_exec(bContext *C, wmOperator *op)
params.use_anim = RNA_boolean_get(op->ptr, "use_anim");
params.anim_offset = RNA_float_get(op->ptr, "anim_offset");
params.vertex_colors = eFBXVertexColorMode(RNA_enum_get(op->ptr, "import_colors"));
params.mtl_name_collision_mode = eFBXMtlNameCollisionMode(
RNA_enum_get(op->ptr, "mtl_name_collision_mode"));
params.reports = op->reports;
@@ -110,6 +126,11 @@ static void ui_fbx_import_settings(const bContext *C, uiLayout *layout, PointerR
col->prop(ptr, "validate_meshes", UI_ITEM_NONE, std::nullopt, ICON_NONE);
}
if (uiLayout *panel = layout->panel(C, "FBX_import_material", true, IFACE_("Materials"))) {
uiLayout *col = &panel->column(false);
col->prop(ptr, "mtl_name_collision_mode", UI_ITEM_NONE, std::nullopt, ICON_NONE);
}
{
PanelLayout panel = layout->panel(C, "FBX_import_anim", true);
panel.header->use_property_split_set(false);
@@ -157,6 +178,14 @@ void WM_OT_fbx_import(wmOperatorType *ot)
FILE_SORT_DEFAULT);
RNA_def_float(ot->srna, "global_scale", 1.0f, 1e-6f, 1e6f, "Scale", "", 0.001f, 1000.0f);
RNA_def_enum(
ot->srna,
"mtl_name_collision_mode",
rna_enum_fbx_mtl_name_collision_mode_items,
int(eFBXMtlNameCollisionMode::MakeUnique),
"Material Name Collision",
"Behavior when the name of an imported material conflicts with an existing material");
RNA_def_enum(ot->srna,
"import_colors",
fbx_vertex_colors_mode,

View File

@@ -18,6 +18,15 @@ struct Mesh;
struct bContext;
struct ReportList;
/**
* Behavior when the name of an imported material
* conflicts with an existing material.
*/
enum class eFBXMtlNameCollisionMode {
MakeUnique = 0,
ReferenceExisting = 1,
};
enum class eFBXVertexColorMode {
None = 0,
sRGB = 1,
@@ -27,6 +36,7 @@ enum class eFBXVertexColorMode {
struct FBXImportParams {
char filepath[FILE_MAX] = "";
float global_scale = 1.0f;
eFBXMtlNameCollisionMode mtl_name_collision_mode = eFBXMtlNameCollisionMode::MakeUnique;
eFBXVertexColorMode vertex_colors = eFBXVertexColorMode::sRGB;
bool validate_meshes = true;
bool use_custom_normals = true;

View File

@@ -8,6 +8,7 @@
#include "BKE_camera.h"
#include "BKE_layer.hh"
#include "BKE_lib_id.hh"
#include "BKE_light.h"
#include "BKE_object.hh"
#include "BKE_report.hh"
@@ -98,9 +99,17 @@ void FbxImportContext::import_globals(Scene *scene) const
void FbxImportContext::import_materials()
{
for (const ufbx_material *fmat : this->fbx.materials) {
Material *mat = io::fbx::import_material(this->bmain, this->base_dir, *fmat);
if (this->params.use_custom_props) {
read_custom_properties(fmat->props, mat->id, this->params.props_enum_as_string);
Material *mat = nullptr;
/* Check if a material with this name already exists in the main database */
if (this->params.mtl_name_collision_mode == eFBXMtlNameCollisionMode::ReferenceExisting) {
mat = (Material *)BKE_libblock_find_name(this->bmain, ID_MA, fmat->name.data);
}
if (mat == nullptr) {
mat = io::fbx::import_material(this->bmain, this->base_dir, *fmat);
if (this->params.use_custom_props) {
read_custom_properties(fmat->props, mat->id, this->params.props_enum_as_string);
}
}
this->mapping.mat_to_material.add(fmat, mat);
}