OBJ importer: Support bump multiplier (normal scale)

This improves compatibility with OBJ/MTL files found in the wild.

(cherry picked from commit 2f3eaa965c)
This commit is contained in:
Ricardo Buring 2025-09-26 12:41:34 +02:00 committed by Rémi Verschelde
parent ccf3976dc5
commit 25415a831e
No known key found for this signature in database
GPG key ID: C3336907360768E1

View file

@ -49,18 +49,17 @@ static Error _parse_material_library(const String &p_path, HashMap<String, Ref<S
String l = f->get_line().strip_edges();
if (l.begins_with("newmtl ")) {
//vertex
// Start of a new material.
current_name = l.replace("newmtl", "").strip_edges();
current.instantiate();
current->set_name(current_name);
material_map[current_name] = current;
} else if (l.begins_with("Ka ")) {
//uv
// Ambient color.
WARN_PRINT("OBJ: Ambient light for material '" + current_name + "' is ignored in PBR");
} else if (l.begins_with("Kd ")) {
//normal
// Diffuse color.
ERR_FAIL_COND_V(current.is_null(), ERR_FILE_CORRUPT);
Vector<String> v = l.split(" ", false);
ERR_FAIL_COND_V(v.size() < 4, ERR_INVALID_DATA);
@ -70,7 +69,7 @@ static Error _parse_material_library(const String &p_path, HashMap<String, Ref<S
c.b = v[3].to_float();
current->set_albedo(c);
} else if (l.begins_with("Ks ")) {
//normal
// Specular color.
ERR_FAIL_COND_V(current.is_null(), ERR_FILE_CORRUPT);
Vector<String> v = l.split(" ", false);
ERR_FAIL_COND_V(v.size() < 4, ERR_INVALID_DATA);
@ -80,14 +79,14 @@ static Error _parse_material_library(const String &p_path, HashMap<String, Ref<S
float metalness = MAX(r, MAX(g, b));
current->set_metallic(metalness);
} else if (l.begins_with("Ns ")) {
//normal
// Specular exponent.
ERR_FAIL_COND_V(current.is_null(), ERR_FILE_CORRUPT);
Vector<String> v = l.split(" ", false);
ERR_FAIL_COND_V(v.size() != 2, ERR_INVALID_DATA);
float s = v[1].to_float();
current->set_metallic((1000.0 - s) / 1000.0);
} else if (l.begins_with("d ")) {
//normal
// Dissolve (1.0 is fully opaque, 0.0 is completely transparent).
ERR_FAIL_COND_V(current.is_null(), ERR_FILE_CORRUPT);
Vector<String> v = l.split(" ", false);
ERR_FAIL_COND_V(v.size() != 2, ERR_INVALID_DATA);
@ -99,7 +98,7 @@ static Error _parse_material_library(const String &p_path, HashMap<String, Ref<S
current->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
}
} else if (l.begins_with("Tr ")) {
//normal
// Transparency (1.0 is completely transparent, 0.0 is fully opaque).
ERR_FAIL_COND_V(current.is_null(), ERR_FILE_CORRUPT);
Vector<String> v = l.split(" ", false);
ERR_FAIL_COND_V(v.size() != 2, ERR_INVALID_DATA);
@ -112,11 +111,11 @@ static Error _parse_material_library(const String &p_path, HashMap<String, Ref<S
}
} else if (l.begins_with("map_Ka ")) {
//uv
// Ambient texture map.
WARN_PRINT("OBJ: Ambient light texture for material '" + current_name + "' is ignored in PBR");
} else if (l.begins_with("map_Kd ")) {
//normal
// Diffuse texture map.
ERR_FAIL_COND_V(current.is_null(), ERR_FILE_CORRUPT);
String p = l.replace("map_Kd", "").replace_char('\\', '/').strip_edges();
@ -136,7 +135,7 @@ static Error _parse_material_library(const String &p_path, HashMap<String, Ref<S
}
} else if (l.begins_with("map_Ks ")) {
//normal
// Specular color texture map.
ERR_FAIL_COND_V(current.is_null(), ERR_FILE_CORRUPT);
String p = l.replace("map_Ks", "").replace_char('\\', '/').strip_edges();
@ -156,7 +155,7 @@ static Error _parse_material_library(const String &p_path, HashMap<String, Ref<S
}
} else if (l.begins_with("map_Ns ")) {
//normal
// Specular exponent texture map.
ERR_FAIL_COND_V(current.is_null(), ERR_FILE_CORRUPT);
String p = l.replace("map_Ns", "").replace_char('\\', '/').strip_edges();
@ -174,18 +173,39 @@ static Error _parse_material_library(const String &p_path, HashMap<String, Ref<S
} else if (r_missing_deps) {
r_missing_deps->push_back(path);
}
} else if (l.begins_with("map_bump ")) {
//normal
} else if (l.begins_with("map_bump ") || l.begins_with("map_Bump ")) {
// Bump texture map.
ERR_FAIL_COND_V(current.is_null(), ERR_FILE_CORRUPT);
String p = l.replace("map_bump", "").replace_char('\\', '/').strip_edges();
String path = base_path.path_join(p);
l = l.begins_with("map_bump ") ? l.trim_prefix("map_bump ") : l.trim_prefix("map_Bump ");
l = l.strip_edges();
// Read path and optional bump multiplier.
String p;
float bm = 1.0;
int bm_pos = l.find("-bm ");
if (bm_pos >= 0) {
int bm_start = bm_pos + 4;
int bm_end = l.find_char(' ', bm_start);
if (bm_end >= 0) {
bm = l.substr(bm_start, bm_end - bm_start).to_float();
p = l.substr(bm_end + 1);
} else { // Bump multiplier ends at end of line.
bm = l.substr(bm_start).to_float();
p = l.substr(0, bm_pos);
}
} else {
p = l;
}
String path = base_path.path_join(p.replace_char('\\', '/').strip_edges());
Ref<Texture2D> texture = ResourceLoader::load(path);
if (texture.is_valid()) {
current->set_feature(StandardMaterial3D::FEATURE_NORMAL_MAPPING, true);
current->set_texture(StandardMaterial3D::TEXTURE_NORMAL, texture);
current->set_normal_scale(bm);
} else if (r_missing_deps) {
r_missing_deps->push_back(path);
}