obj: implement support for PBR .mtl extensions
Implement import & export support for "PBR extensions" in .mtl files (T101029, also fixes T86736). Newly supported parameters: - Roughness (Pr, map_Pr) - Metallic (Pm, map_Pm) - Sheen (Ps, map_Ps) - Clearcoat thickness (Pc) and roughness (Pcr) - Anisotropy (aniso) and rotation (anisor) - Transmittance (Tf / Kt) Exporter has an option to enable these additional PBR parameters export; defaults to off since not all software understands that. Exporter UI tweaked and all material-related options were put into their own separate box. Added/extended test files in Subversion repository for test coverage.
This commit is contained in:
parent
3839a4dd84
commit
a99a62231e
Notes:
blender-bot
2025-02-14 01:07:08 +00:00
Referenced by issue #101029, OBJ: support PBR extensions in import/export
@ -93,6 +93,7 @@ static int wm_obj_export_exec(bContext *C, wmOperator *op)
|
||||
export_params.path_mode = RNA_enum_get(op->ptr, "path_mode");
|
||||
export_params.export_triangulated_mesh = RNA_boolean_get(op->ptr, "export_triangulated_mesh");
|
||||
export_params.export_curves_as_nurbs = RNA_boolean_get(op->ptr, "export_curves_as_nurbs");
|
||||
export_params.export_pbr_extensions = RNA_boolean_get(op->ptr, "export_pbr_extensions");
|
||||
|
||||
export_params.export_object_groups = RNA_boolean_get(op->ptr, "export_object_groups");
|
||||
export_params.export_material_groups = RNA_boolean_get(op->ptr, "export_material_groups");
|
||||
@ -118,7 +119,6 @@ static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr)
|
||||
|
||||
/* Object Transform options. */
|
||||
box = uiLayoutBox(layout);
|
||||
uiItemL(box, IFACE_("Object Properties"), ICON_OBJECT_DATA);
|
||||
col = uiLayoutColumn(box, false);
|
||||
sub = uiLayoutColumnWithHeading(col, false, IFACE_("Limit to"));
|
||||
uiItemR(sub, imfptr, "export_selected_objects", 0, IFACE_("Selected Only"), ICON_NONE);
|
||||
@ -134,27 +134,31 @@ static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr)
|
||||
sub = uiLayoutColumnWithHeading(col, false, IFACE_("Objects"));
|
||||
uiItemR(sub, imfptr, "apply_modifiers", 0, IFACE_("Apply Modifiers"), ICON_NONE);
|
||||
uiItemR(sub, imfptr, "export_eval_mode", 0, IFACE_("Properties"), ICON_NONE);
|
||||
sub = uiLayoutColumn(sub, false);
|
||||
uiLayoutSetEnabled(sub, export_materials);
|
||||
uiItemR(sub, imfptr, "path_mode", 0, IFACE_("Path Mode"), ICON_NONE);
|
||||
|
||||
/* Options for what to write. */
|
||||
/* Geometry options. */
|
||||
box = uiLayoutBox(layout);
|
||||
uiItemL(box, IFACE_("Geometry"), ICON_EXPORT);
|
||||
col = uiLayoutColumn(box, false);
|
||||
sub = uiLayoutColumnWithHeading(col, false, IFACE_("Export"));
|
||||
sub = uiLayoutColumnWithHeading(col, false, IFACE_("Geometry"));
|
||||
uiItemR(sub, imfptr, "export_uv", 0, IFACE_("UV Coordinates"), ICON_NONE);
|
||||
uiItemR(sub, imfptr, "export_normals", 0, IFACE_("Normals"), ICON_NONE);
|
||||
uiItemR(sub, imfptr, "export_colors", 0, IFACE_("Colors"), ICON_NONE);
|
||||
uiItemR(sub, imfptr, "export_materials", 0, IFACE_("Materials"), ICON_NONE);
|
||||
uiItemR(sub, imfptr, "export_triangulated_mesh", 0, IFACE_("Triangulated Mesh"), ICON_NONE);
|
||||
uiItemR(sub, imfptr, "export_curves_as_nurbs", 0, IFACE_("Curves as NURBS"), ICON_NONE);
|
||||
|
||||
/* Material options. */
|
||||
box = uiLayoutBox(layout);
|
||||
col = uiLayoutColumn(box, false);
|
||||
sub = uiLayoutColumnWithHeading(col, false, IFACE_("Materials"));
|
||||
uiItemR(sub, imfptr, "export_materials", 0, IFACE_("Export"), ICON_NONE);
|
||||
sub = uiLayoutColumn(sub, false);
|
||||
uiLayoutSetEnabled(sub, export_materials);
|
||||
uiItemR(sub, imfptr, "export_pbr_extensions", 0, IFACE_("PBR Extensions"), ICON_NONE);
|
||||
uiItemR(sub, imfptr, "path_mode", 0, IFACE_("Path Mode"), ICON_NONE);
|
||||
|
||||
/* Grouping options. */
|
||||
box = uiLayoutBox(layout);
|
||||
uiItemL(box, IFACE_("Grouping"), ICON_GROUP);
|
||||
col = uiLayoutColumn(box, false);
|
||||
sub = uiLayoutColumnWithHeading(col, false, IFACE_("Export"));
|
||||
sub = uiLayoutColumnWithHeading(col, false, IFACE_("Grouping"));
|
||||
uiItemR(sub, imfptr, "export_object_groups", 0, IFACE_("Object Groups"), ICON_NONE);
|
||||
uiItemR(sub, imfptr, "export_material_groups", 0, IFACE_("Material Groups"), ICON_NONE);
|
||||
uiItemR(sub, imfptr, "export_vertex_groups", 0, IFACE_("Vertex Groups"), ICON_NONE);
|
||||
@ -165,14 +169,13 @@ static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr)
|
||||
|
||||
/* Animation options. */
|
||||
box = uiLayoutBox(layout);
|
||||
uiItemL(box, IFACE_("Animation"), ICON_ANIM);
|
||||
col = uiLayoutColumn(box, false);
|
||||
sub = uiLayoutColumn(col, false);
|
||||
uiItemR(sub, imfptr, "export_animation", 0, NULL, ICON_NONE);
|
||||
sub = uiLayoutColumnWithHeading(col, false, IFACE_("Animation"));
|
||||
uiItemR(sub, imfptr, "export_animation", 0, IFACE_("Export"), ICON_NONE);
|
||||
sub = uiLayoutColumn(sub, true);
|
||||
uiLayoutSetEnabled(sub, export_animation);
|
||||
uiItemR(sub, imfptr, "start_frame", 0, IFACE_("Frame Start"), ICON_NONE);
|
||||
uiItemR(sub, imfptr, "end_frame", 0, IFACE_("End"), ICON_NONE);
|
||||
uiLayoutSetEnabled(sub, export_animation);
|
||||
}
|
||||
|
||||
static void wm_obj_export_draw(bContext *UNUSED(C), wmOperator *op)
|
||||
@ -336,6 +339,12 @@ void WM_OT_obj_export(struct wmOperatorType *ot)
|
||||
"Export Materials",
|
||||
"Export MTL library. There must be a Principled-BSDF node for image textures to "
|
||||
"be exported to the MTL file");
|
||||
RNA_def_boolean(ot->srna,
|
||||
"export_pbr_extensions",
|
||||
false,
|
||||
"Export Materials with PBR Extensions",
|
||||
"Export MTL library using PBR extensions (roughness, metallic, sheen, "
|
||||
"clearcoat, anisotropy, transmission)");
|
||||
RNA_def_enum(ot->srna,
|
||||
"path_mode",
|
||||
io_obj_path_mode,
|
||||
|
@ -48,18 +48,15 @@ struct OBJExportParams {
|
||||
bool export_triangulated_mesh;
|
||||
bool export_curves_as_nurbs;
|
||||
ePathReferenceMode path_mode;
|
||||
bool export_pbr_extensions;
|
||||
|
||||
/* Grouping options. */
|
||||
bool export_object_groups;
|
||||
bool export_material_groups;
|
||||
bool export_vertex_groups;
|
||||
/**
|
||||
* Calculate smooth groups from sharp edges.
|
||||
*/
|
||||
/* Calculate smooth groups from sharp edges. */
|
||||
bool export_smooth_groups;
|
||||
/**
|
||||
* Create bitflags instead of the default "0"/"1" group IDs.
|
||||
*/
|
||||
/* Create bitflags instead of the default "0"/"1" group IDs. */
|
||||
bool smooth_groups_bitflags;
|
||||
};
|
||||
|
||||
|
@ -493,11 +493,14 @@ void OBJWriter::write_nurbs_curve(FormatHandler &fh, const OBJCurve &obj_nurbs_d
|
||||
|
||||
static const char *tex_map_type_to_string[] = {
|
||||
"map_Kd",
|
||||
"map_Pm",
|
||||
"map_Ks",
|
||||
"map_Ns",
|
||||
"map_d",
|
||||
"map_Pr",
|
||||
"map_Ps",
|
||||
"map_refl",
|
||||
"map_Ke",
|
||||
"map_d",
|
||||
"map_Bump",
|
||||
};
|
||||
BLI_STATIC_ASSERT(ARRAY_SIZE(tex_map_type_to_string) == (int)MTLTexMapType::Count,
|
||||
@ -553,16 +556,20 @@ StringRefNull MTLWriter::mtl_file_path() const
|
||||
return mtl_filepath_;
|
||||
}
|
||||
|
||||
void MTLWriter::write_bsdf_properties(const MTLMaterial &mtl)
|
||||
void MTLWriter::write_bsdf_properties(const MTLMaterial &mtl, bool write_pbr)
|
||||
{
|
||||
/* For various material properties, we only capture information
|
||||
* coming from the texture, or the default value of the socket.
|
||||
* When the texture is present, do not emit the default value. */
|
||||
if (!mtl.tex_map_of_type(MTLTexMapType::SpecularExponent).is_valid()) {
|
||||
fmt_handler_.write_mtl_float("Ns", mtl.spec_exponent);
|
||||
|
||||
/* Do not write Ns & Ka when writing in PBR mode. */
|
||||
if (!write_pbr) {
|
||||
if (!mtl.tex_map_of_type(MTLTexMapType::SpecularExponent).is_valid()) {
|
||||
fmt_handler_.write_mtl_float("Ns", mtl.spec_exponent);
|
||||
}
|
||||
fmt_handler_.write_mtl_float3(
|
||||
"Ka", mtl.ambient_color.x, mtl.ambient_color.y, mtl.ambient_color.z);
|
||||
}
|
||||
fmt_handler_.write_mtl_float3(
|
||||
"Ka", mtl.ambient_color.x, mtl.ambient_color.y, mtl.ambient_color.z);
|
||||
if (!mtl.tex_map_of_type(MTLTexMapType::Color).is_valid()) {
|
||||
fmt_handler_.write_mtl_float3("Kd", mtl.color.x, mtl.color.y, mtl.color.z);
|
||||
}
|
||||
@ -578,6 +585,35 @@ void MTLWriter::write_bsdf_properties(const MTLMaterial &mtl)
|
||||
fmt_handler_.write_mtl_float("d", mtl.alpha);
|
||||
}
|
||||
fmt_handler_.write_mtl_illum(mtl.illum_mode);
|
||||
|
||||
if (write_pbr) {
|
||||
if (!mtl.tex_map_of_type(MTLTexMapType::Roughness).is_valid() && mtl.roughness >= 0.0f) {
|
||||
fmt_handler_.write_mtl_float("Pr", mtl.roughness);
|
||||
}
|
||||
if (!mtl.tex_map_of_type(MTLTexMapType::Metallic).is_valid() && mtl.metallic >= 0.0f) {
|
||||
fmt_handler_.write_mtl_float("Pm", mtl.metallic);
|
||||
}
|
||||
if (!mtl.tex_map_of_type(MTLTexMapType::Sheen).is_valid() && mtl.sheen >= 0.0f) {
|
||||
fmt_handler_.write_mtl_float("Ps", mtl.sheen);
|
||||
}
|
||||
if (mtl.cc_thickness >= 0.0f) {
|
||||
fmt_handler_.write_mtl_float("Pc", mtl.cc_thickness);
|
||||
}
|
||||
if (mtl.cc_roughness >= 0.0f) {
|
||||
fmt_handler_.write_mtl_float("Pcr", mtl.cc_roughness);
|
||||
}
|
||||
if (mtl.aniso >= 0.0f) {
|
||||
fmt_handler_.write_mtl_float("aniso", mtl.aniso);
|
||||
}
|
||||
if (mtl.aniso_rot >= 0.0f) {
|
||||
fmt_handler_.write_mtl_float("anisor", mtl.aniso_rot);
|
||||
}
|
||||
if (mtl.transmit_color.x > 0.0f || mtl.transmit_color.y > 0.0f ||
|
||||
mtl.transmit_color.z > 0.0f) {
|
||||
fmt_handler_.write_mtl_float3(
|
||||
"Tf", mtl.transmit_color.x, mtl.transmit_color.y, mtl.transmit_color.z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MTLWriter::write_texture_map(const MTLMaterial &mtl_material,
|
||||
@ -608,9 +644,21 @@ void MTLWriter::write_texture_map(const MTLMaterial &mtl_material,
|
||||
fmt_handler_.write_mtl_map(tex_map_type_to_string[(int)texture_key], options, path);
|
||||
}
|
||||
|
||||
static bool is_pbr_map(MTLTexMapType type)
|
||||
{
|
||||
return type == MTLTexMapType::Metallic || type == MTLTexMapType::Roughness ||
|
||||
type == MTLTexMapType::Sheen;
|
||||
}
|
||||
|
||||
static bool is_non_pbr_map(MTLTexMapType type)
|
||||
{
|
||||
return type == MTLTexMapType::SpecularExponent || type == MTLTexMapType::Reflection;
|
||||
}
|
||||
|
||||
void MTLWriter::write_materials(const char *blen_filepath,
|
||||
ePathReferenceMode path_mode,
|
||||
const char *dest_dir)
|
||||
const char *dest_dir,
|
||||
bool write_pbr)
|
||||
{
|
||||
if (mtlmaterials_.size() == 0) {
|
||||
return;
|
||||
@ -628,12 +676,18 @@ void MTLWriter::write_materials(const char *blen_filepath,
|
||||
for (const MTLMaterial &mtlmat : mtlmaterials_) {
|
||||
fmt_handler_.write_string("");
|
||||
fmt_handler_.write_mtl_newmtl(mtlmat.name);
|
||||
write_bsdf_properties(mtlmat);
|
||||
write_bsdf_properties(mtlmat, write_pbr);
|
||||
for (int key = 0; key < (int)MTLTexMapType::Count; key++) {
|
||||
const MTLTexMap &tex = mtlmat.texture_maps[key];
|
||||
if (!tex.is_valid()) {
|
||||
continue;
|
||||
}
|
||||
if (!write_pbr && is_pbr_map((MTLTexMapType)key)) {
|
||||
continue;
|
||||
}
|
||||
if (write_pbr && is_non_pbr_map((MTLTexMapType)key)) {
|
||||
continue;
|
||||
}
|
||||
write_texture_map(
|
||||
mtlmat, (MTLTexMapType)key, tex, blen_filedir, dest_dir, path_mode, copy_set);
|
||||
}
|
||||
|
@ -186,7 +186,8 @@ class MTLWriter : NonMovable, NonCopyable {
|
||||
*/
|
||||
void write_materials(const char *blen_filepath,
|
||||
ePathReferenceMode path_mode,
|
||||
const char *dest_dir);
|
||||
const char *dest_dir,
|
||||
bool write_pbr);
|
||||
StringRefNull mtl_file_path() const;
|
||||
/**
|
||||
* Add the materials of the given object to #MTLWriter, de-duplicating
|
||||
@ -203,7 +204,7 @@ class MTLWriter : NonMovable, NonCopyable {
|
||||
/**
|
||||
* Write properties sourced from p-BSDF node or #Object.Material.
|
||||
*/
|
||||
void write_bsdf_properties(const MTLMaterial &mtl_material);
|
||||
void write_bsdf_properties(const MTLMaterial &mtl_material, bool write_pbr);
|
||||
/**
|
||||
* Write a texture map in the form "map_XX -s 1. 1. 1. -o 0. 0. 0. [-bm 1.] path/to/image".
|
||||
*/
|
||||
|
@ -23,11 +23,14 @@ namespace blender::io::obj {
|
||||
|
||||
const char *tex_map_type_to_socket_id[] = {
|
||||
"Base Color",
|
||||
"Specular",
|
||||
"Roughness",
|
||||
"Alpha",
|
||||
"Metallic",
|
||||
"Specular",
|
||||
"Roughness", /* Map specular exponent to roughness. */
|
||||
"Roughness",
|
||||
"Sheen",
|
||||
"Metallic", /* Map reflection to metallic. */
|
||||
"Emission",
|
||||
"Alpha",
|
||||
"Normal",
|
||||
};
|
||||
BLI_STATIC_ASSERT(ARRAY_SIZE(tex_map_type_to_socket_id) == (int)MTLTexMapType::Count,
|
||||
@ -188,7 +191,6 @@ static void store_bsdf_properties(const bNode *bsdf_node,
|
||||
const Material *material,
|
||||
MTLMaterial &r_mtl_mat)
|
||||
{
|
||||
/* If p-BSDF is not present, fallback to #Object.Material. */
|
||||
float roughness = material->roughness;
|
||||
if (bsdf_node) {
|
||||
copy_property_from_node(SOCK_FLOAT, bsdf_node, "Roughness", {&roughness, 1});
|
||||
@ -231,6 +233,22 @@ static void store_bsdf_properties(const bNode *bsdf_node,
|
||||
}
|
||||
mul_v3_fl(emission_col, emission_strength);
|
||||
|
||||
float sheen = -1.0f;
|
||||
float clearcoat = -1.0f;
|
||||
float clearcoat_roughness = -1.0f;
|
||||
float aniso = -1.0f;
|
||||
float aniso_rot = -1.0f;
|
||||
float transmission = -1.0f;
|
||||
if (bsdf_node) {
|
||||
copy_property_from_node(SOCK_FLOAT, bsdf_node, "Sheen", {&sheen, 1});
|
||||
copy_property_from_node(SOCK_FLOAT, bsdf_node, "Clearcoat", {&clearcoat, 1});
|
||||
copy_property_from_node(
|
||||
SOCK_FLOAT, bsdf_node, "Clearcoat Roughness", {&clearcoat_roughness, 1});
|
||||
copy_property_from_node(SOCK_FLOAT, bsdf_node, "Anisotropic", {&aniso, 1});
|
||||
copy_property_from_node(SOCK_FLOAT, bsdf_node, "Anisotropic Rotation", {&aniso_rot, 1});
|
||||
copy_property_from_node(SOCK_FLOAT, bsdf_node, "Transmission", {&transmission, 1});
|
||||
}
|
||||
|
||||
/* See https://wikipedia.org/wiki/Wavefront_.obj_file for all possible values of `illum`. */
|
||||
/* Highlight on. */
|
||||
int illum = 2;
|
||||
@ -266,6 +284,14 @@ static void store_bsdf_properties(const bNode *bsdf_node,
|
||||
r_mtl_mat.ior = refraction_index;
|
||||
r_mtl_mat.alpha = alpha;
|
||||
r_mtl_mat.illum_mode = illum;
|
||||
r_mtl_mat.roughness = roughness;
|
||||
r_mtl_mat.metallic = metallic;
|
||||
r_mtl_mat.sheen = sheen;
|
||||
r_mtl_mat.cc_thickness = clearcoat;
|
||||
r_mtl_mat.cc_roughness = clearcoat_roughness;
|
||||
r_mtl_mat.aniso = aniso;
|
||||
r_mtl_mat.aniso_rot = aniso_rot;
|
||||
r_mtl_mat.transmit_color = {transmission, transmission, transmission};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -15,11 +15,14 @@ namespace blender::io::obj {
|
||||
|
||||
enum class MTLTexMapType {
|
||||
Color = 0,
|
||||
Metallic,
|
||||
Specular,
|
||||
SpecularExponent,
|
||||
Alpha,
|
||||
Roughness,
|
||||
Sheen,
|
||||
Reflection,
|
||||
Emission,
|
||||
Alpha,
|
||||
Normal,
|
||||
Count
|
||||
};
|
||||
@ -63,6 +66,15 @@ struct MTLMaterial {
|
||||
float3 emission_color{-1.0f}; /* `Ke` */
|
||||
float ior{-1.0f}; /* `Ni` */
|
||||
float alpha{-1.0f}; /* `d` */
|
||||
float3 transmit_color{-1.0f}; /* `Kt` / `Tf` */
|
||||
float roughness{-1.0f}; /* `Pr` */
|
||||
float metallic{-1.0f}; /* `Pm` */
|
||||
float sheen{-1.0f}; /* `Ps` */
|
||||
float cc_thickness{-1.0f}; /* `Pc` */
|
||||
float cc_roughness{-1.0f}; /* `Pcr` */
|
||||
float aniso{-1.0f}; /* `aniso` */
|
||||
float aniso_rot{-1.0f}; /* `anisor` */
|
||||
|
||||
int illum_mode{-1};
|
||||
MTLTexMap texture_maps[(int)MTLTexMapType::Count];
|
||||
/* Only used for Normal Map node: `map_Bump`. */
|
||||
|
@ -293,7 +293,10 @@ void export_frame(Depsgraph *depsgraph, const OBJExportParams &export_params, co
|
||||
}
|
||||
BLI_path_slash_native(dest_dir);
|
||||
BLI_path_normalize(nullptr, dest_dir);
|
||||
mtl_writer->write_materials(export_params.blen_filepath, export_params.path_mode, dest_dir);
|
||||
mtl_writer->write_materials(export_params.blen_filepath,
|
||||
export_params.path_mode,
|
||||
dest_dir,
|
||||
export_params.export_pbr_extensions);
|
||||
}
|
||||
write_nurbs_curve_objects(std::move(exportable_as_nurbs), *frame_writer);
|
||||
}
|
||||
|
@ -617,6 +617,15 @@ static MTLTexMapType mtl_line_start_to_texture_type(const char *&p, const char *
|
||||
parse_keyword(p, end, "map_bump")) {
|
||||
return MTLTexMapType::Normal;
|
||||
}
|
||||
if (parse_keyword(p, end, "map_Pr")) {
|
||||
return MTLTexMapType::Roughness;
|
||||
}
|
||||
if (parse_keyword(p, end, "map_Pm")) {
|
||||
return MTLTexMapType::Metallic;
|
||||
}
|
||||
if (parse_keyword(p, end, "map_Ps")) {
|
||||
return MTLTexMapType::Sheen;
|
||||
}
|
||||
return MTLTexMapType::Count;
|
||||
}
|
||||
|
||||
@ -806,6 +815,30 @@ void MTLParser::parse_and_store(Map<string, std::unique_ptr<MTLMaterial>> &r_mat
|
||||
parse_float(p, end, 1.0f, val);
|
||||
material->illum_mode = val;
|
||||
}
|
||||
else if (parse_keyword(p, end, "Pr")) {
|
||||
parse_float(p, end, 0.5f, material->roughness);
|
||||
}
|
||||
else if (parse_keyword(p, end, "Pm")) {
|
||||
parse_float(p, end, 0.0f, material->metallic);
|
||||
}
|
||||
else if (parse_keyword(p, end, "Ps")) {
|
||||
parse_float(p, end, 0.0f, material->sheen);
|
||||
}
|
||||
else if (parse_keyword(p, end, "Pc")) {
|
||||
parse_float(p, end, 0.0f, material->cc_thickness);
|
||||
}
|
||||
else if (parse_keyword(p, end, "Pcr")) {
|
||||
parse_float(p, end, 0.0f, material->cc_roughness);
|
||||
}
|
||||
else if (parse_keyword(p, end, "aniso")) {
|
||||
parse_float(p, end, 0.0f, material->aniso);
|
||||
}
|
||||
else if (parse_keyword(p, end, "anisor")) {
|
||||
parse_float(p, end, 0.0f, material->aniso_rot);
|
||||
}
|
||||
else if (parse_keyword(p, end, "Kt") || parse_keyword(p, end, "Tf")) {
|
||||
parse_floats(p, end, 0.0f, material->transmit_color, 3);
|
||||
}
|
||||
else {
|
||||
parse_texture_map(p, end, material, mtl_dir_path_);
|
||||
}
|
||||
|
@ -284,6 +284,14 @@ static void set_bsdf_socket_values(bNode *bsdf, Material *mat, const MTLMaterial
|
||||
alpha = 1.0f;
|
||||
}
|
||||
|
||||
/* PBR values, when present, override the ones calculated above. */
|
||||
if (mtl_mat.roughness >= 0) {
|
||||
roughness = mtl_mat.roughness;
|
||||
}
|
||||
if (mtl_mat.metallic >= 0) {
|
||||
metallic = mtl_mat.metallic;
|
||||
}
|
||||
|
||||
float3 base_color = mtl_mat.color;
|
||||
if (base_color.x >= 0 && base_color.y >= 0 && base_color.z >= 0) {
|
||||
set_property_of_socket(SOCK_RGBA, "Base Color", {base_color, 3}, bsdf);
|
||||
@ -314,6 +322,30 @@ static void set_bsdf_socket_values(bNode *bsdf, Material *mat, const MTLMaterial
|
||||
if (do_tranparency || (alpha >= 0.0f && alpha < 1.0f)) {
|
||||
mat->blend_method = MA_BM_BLEND;
|
||||
}
|
||||
|
||||
if (mtl_mat.sheen >= 0) {
|
||||
set_property_of_socket(SOCK_FLOAT, "Sheen", {mtl_mat.sheen}, bsdf);
|
||||
}
|
||||
if (mtl_mat.cc_thickness >= 0) {
|
||||
set_property_of_socket(SOCK_FLOAT, "Clearcoat", {mtl_mat.cc_thickness}, bsdf);
|
||||
}
|
||||
if (mtl_mat.cc_roughness >= 0) {
|
||||
set_property_of_socket(SOCK_FLOAT, "Clearcoat Roughness", {mtl_mat.cc_roughness}, bsdf);
|
||||
}
|
||||
if (mtl_mat.aniso >= 0) {
|
||||
set_property_of_socket(SOCK_FLOAT, "Anisotropic", {mtl_mat.aniso}, bsdf);
|
||||
}
|
||||
if (mtl_mat.aniso_rot >= 0) {
|
||||
set_property_of_socket(SOCK_FLOAT, "Anisotropic Rotation", {mtl_mat.aniso_rot}, bsdf);
|
||||
}
|
||||
|
||||
/* Transmission: average of transmission color. */
|
||||
float transmission = (mtl_mat.transmit_color[0] + mtl_mat.transmit_color[1] +
|
||||
mtl_mat.transmit_color[2]) /
|
||||
3;
|
||||
if (transmission >= 0) {
|
||||
set_property_of_socket(SOCK_FLOAT, "Transmission", {transmission}, bsdf);
|
||||
}
|
||||
}
|
||||
|
||||
static void add_image_textures(Main *bmain,
|
||||
|
@ -527,4 +527,27 @@ TEST_F(obj_exporter_regression_test, all_objects_mat_groups)
|
||||
_export.params);
|
||||
}
|
||||
|
||||
TEST_F(obj_exporter_regression_test, materials_without_pbr)
|
||||
{
|
||||
OBJExportParamsDefault _export;
|
||||
_export.params.export_normals = false;
|
||||
_export.params.path_mode = PATH_REFERENCE_RELATIVE;
|
||||
compare_obj_export_to_golden("io_tests/blend_geometry/materials_pbr.blend",
|
||||
"io_tests/obj/materials_without_pbr.obj",
|
||||
"io_tests/obj/materials_without_pbr.mtl",
|
||||
_export.params);
|
||||
}
|
||||
|
||||
TEST_F(obj_exporter_regression_test, materials_pbr)
|
||||
{
|
||||
OBJExportParamsDefault _export;
|
||||
_export.params.export_normals = false;
|
||||
_export.params.path_mode = PATH_REFERENCE_RELATIVE;
|
||||
_export.params.export_pbr_extensions = true;
|
||||
compare_obj_export_to_golden("io_tests/blend_geometry/materials_pbr.blend",
|
||||
"io_tests/obj/materials_pbr.obj",
|
||||
"io_tests/obj/materials_pbr.mtl",
|
||||
_export.params);
|
||||
}
|
||||
|
||||
} // namespace blender::io::obj
|
||||
|
@ -31,6 +31,7 @@ struct OBJExportParamsDefault {
|
||||
params.path_mode = PATH_REFERENCE_AUTO;
|
||||
params.export_triangulated_mesh = false;
|
||||
params.export_curves_as_nurbs = false;
|
||||
params.export_pbr_extensions = false;
|
||||
|
||||
params.export_object_groups = false;
|
||||
params.export_material_groups = false;
|
||||
|
@ -54,11 +54,19 @@ class obj_mtl_parser_test : public testing::Test {
|
||||
EXPECT_V3_NEAR(exp.color, got.color, tol);
|
||||
EXPECT_V3_NEAR(exp.spec_color, got.spec_color, tol);
|
||||
EXPECT_V3_NEAR(exp.emission_color, got.emission_color, tol);
|
||||
EXPECT_V3_NEAR(exp.transmit_color, got.transmit_color, tol);
|
||||
EXPECT_NEAR(exp.spec_exponent, got.spec_exponent, tol);
|
||||
EXPECT_NEAR(exp.ior, got.ior, tol);
|
||||
EXPECT_NEAR(exp.alpha, got.alpha, tol);
|
||||
EXPECT_NEAR(exp.normal_strength, got.normal_strength, tol);
|
||||
EXPECT_EQ(exp.illum_mode, got.illum_mode);
|
||||
EXPECT_NEAR(exp.roughness, got.roughness, tol);
|
||||
EXPECT_NEAR(exp.metallic, got.metallic, tol);
|
||||
EXPECT_NEAR(exp.sheen, got.sheen, tol);
|
||||
EXPECT_NEAR(exp.cc_thickness, got.cc_thickness, tol);
|
||||
EXPECT_NEAR(exp.cc_roughness, got.cc_roughness, tol);
|
||||
EXPECT_NEAR(exp.aniso, got.aniso, tol);
|
||||
EXPECT_NEAR(exp.aniso_rot, got.aniso_rot, tol);
|
||||
for (int key = 0; key < (int)MTLTexMapType::Count; key++) {
|
||||
const MTLTexMap &exp_tex = exp.texture_maps[key];
|
||||
const MTLTexMap &got_tex = got.texture_maps[key];
|
||||
@ -222,6 +230,15 @@ TEST_F(obj_mtl_parser_test, materials)
|
||||
mat[4].ior = 1.5;
|
||||
mat[4].alpha = 0.5;
|
||||
mat[4].normal_strength = 0.1f;
|
||||
mat[4].transmit_color = {0.1f, 0.3f, 0.5f};
|
||||
mat[4].normal_strength = 0.1f;
|
||||
mat[4].roughness = 0.2f;
|
||||
mat[4].metallic = 0.3f;
|
||||
mat[4].sheen = 0.4f;
|
||||
mat[4].cc_thickness = 0.5f;
|
||||
mat[4].cc_roughness = 0.6f;
|
||||
mat[4].aniso = 0.7f;
|
||||
mat[4].aniso_rot = 0.8f;
|
||||
{
|
||||
MTLTexMap &kd = mat[4].tex_map_of_type(MTLTexMapType::Color);
|
||||
kd.image_path = "sometex_d.png";
|
||||
@ -254,4 +271,75 @@ TEST_F(obj_mtl_parser_test, materials)
|
||||
check("materials.mtl", mat, ARRAY_SIZE(mat));
|
||||
}
|
||||
|
||||
TEST_F(obj_mtl_parser_test, materials_without_pbr)
|
||||
{
|
||||
MTLMaterial mat[2];
|
||||
mat[0].name = "Mat1";
|
||||
mat[0].spec_exponent = 360.0f;
|
||||
mat[0].ambient_color = {0.9f, 0.9f, 0.9f};
|
||||
mat[0].color = {0.8f, 0.276449f, 0.101911f};
|
||||
mat[0].spec_color = {0.25f, 0.25f, 0.25f};
|
||||
mat[0].emission_color = {0, 0, 0};
|
||||
mat[0].ior = 1.45f;
|
||||
mat[0].alpha = 1;
|
||||
mat[0].illum_mode = 3;
|
||||
|
||||
mat[1].name = "Mat2";
|
||||
mat[1].ambient_color = {1, 1, 1};
|
||||
mat[1].color = {0.8f, 0.8f, 0.8f};
|
||||
mat[1].spec_color = {0.5f, 0.5f, 0.5f};
|
||||
mat[1].ior = 1.45f;
|
||||
mat[1].alpha = 1;
|
||||
mat[1].illum_mode = 2;
|
||||
{
|
||||
MTLTexMap &ns = mat[1].tex_map_of_type(MTLTexMapType::SpecularExponent);
|
||||
ns.image_path = "../blend_geometry/texture_roughness.png";
|
||||
MTLTexMap &ke = mat[1].tex_map_of_type(MTLTexMapType::Emission);
|
||||
ke.image_path = "../blend_geometry/texture_illum.png";
|
||||
}
|
||||
|
||||
check("materials_without_pbr.mtl", mat, ARRAY_SIZE(mat));
|
||||
}
|
||||
|
||||
TEST_F(obj_mtl_parser_test, materials_pbr)
|
||||
{
|
||||
MTLMaterial mat[2];
|
||||
mat[0].name = "Mat1";
|
||||
mat[0].color = {0.8f, 0.276449f, 0.101911f};
|
||||
mat[0].spec_color = {0.25f, 0.25f, 0.25f};
|
||||
mat[0].emission_color = {0, 0, 0};
|
||||
mat[0].ior = 1.45f;
|
||||
mat[0].alpha = 1;
|
||||
mat[0].illum_mode = 3;
|
||||
mat[0].roughness = 0.4f;
|
||||
mat[0].metallic = 0.9f;
|
||||
mat[0].sheen = 0.3f;
|
||||
mat[0].cc_thickness = 0.393182f;
|
||||
mat[0].cc_roughness = 0.05f;
|
||||
mat[0].aniso = 0.2f;
|
||||
mat[0].aniso_rot = 0.0f;
|
||||
|
||||
mat[1].name = "Mat2";
|
||||
mat[1].color = {0.8f, 0.8f, 0.8f};
|
||||
mat[1].spec_color = {0.5f, 0.5f, 0.5f};
|
||||
mat[1].ior = 1.45f;
|
||||
mat[1].alpha = 1;
|
||||
mat[1].illum_mode = 2;
|
||||
mat[1].metallic = 0.0f;
|
||||
mat[1].cc_thickness = 0.3f;
|
||||
mat[1].cc_roughness = 0.4f;
|
||||
mat[1].aniso = 0.8f;
|
||||
mat[1].aniso_rot = 0.7f;
|
||||
{
|
||||
MTLTexMap &pr = mat[1].tex_map_of_type(MTLTexMapType::Roughness);
|
||||
pr.image_path = "../blend_geometry/texture_roughness.png";
|
||||
MTLTexMap &ps = mat[1].tex_map_of_type(MTLTexMapType::Sheen);
|
||||
ps.image_path = "../blend_geometry/texture_checker.png";
|
||||
MTLTexMap &ke = mat[1].tex_map_of_type(MTLTexMapType::Emission);
|
||||
ke.image_path = "../blend_geometry/texture_illum.png";
|
||||
}
|
||||
|
||||
check("materials_pbr.mtl", mat, ARRAY_SIZE(mat));
|
||||
}
|
||||
|
||||
} // namespace blender::io::obj
|
||||
|
Loading…
x
Reference in New Issue
Block a user