diff --git a/doc/classes/BaseMaterial3D.xml b/doc/classes/BaseMaterial3D.xml
index b4a256c54f94..e012c8a94b5f 100644
--- a/doc/classes/BaseMaterial3D.xml
+++ b/doc/classes/BaseMaterial3D.xml
@@ -64,6 +64,7 @@
Texture to multiply by [member albedo_color]. Used for basic texturing of objects.
If the texture appears unexpectedly too dark or too bright, check [member albedo_texture_force_srgb].
+ [b]Note:[/b] When assigned in the editor, other textures' properties will automatically be set based on their texture name. For example, if you assign [code]res://levels/wood_albedo.png[/code] to [member albedo_texture], then [code]res://levels/wood_normal.png[/code] will be assigned to [member normal_texture] and so on. This is based on common texture name conventions, so most texture websites should be handled automatically out of the box. This behavior can be disabled by setting [member EditorSettings.interface/inspector/auto_assign_pbr_material_textures] to [code]false[/code].
If [code]true[/code], forces a conversion of the [member albedo_texture] from sRGB color space to linear color space. See also [member vertex_color_is_srgb].
@@ -107,6 +108,7 @@
Texture that defines the amount of ambient occlusion for a given point on the object.
+ [b]Note:[/b] When assigned in the editor, other textures' properties will automatically be set based on their texture name. For example, if you assign [code]res://levels/wood_ao.png[/code] to [member ao_texture], then [code]res://levels/wood_normal.png[/code] will be assigned to [member normal_texture] and so on. This is based on common texture name conventions, so most texture websites should be handled automatically out of the box. This behavior can be disabled by setting [member EditorSettings.interface/inspector/auto_assign_pbr_material_textures] to [code]false[/code].
Specifies the channel of the [member ao_texture] in which the ambient occlusion information is stored. This is useful when you store the information for multiple effects in a single texture. For example if you stored metallic in the red channel, roughness in the blue, and ambient occlusion in the green you could reduce the number of textures you use.
@@ -119,6 +121,7 @@
Texture used to control the backlight effect per-pixel. Added to [member backlight].
+ [b]Note:[/b] When assigned in the editor, other textures' properties will automatically be set based on their texture name. For example, if you assign [code]res://levels/wood_backlight.png[/code] to [member backlight_texture], then [code]res://levels/wood_normal.png[/code] will be assigned to [member normal_texture] and so on. This is based on common texture name conventions, so most texture websites should be handled automatically out of the box. This behavior can be disabled by setting [member EditorSettings.interface/inspector/auto_assign_pbr_material_textures] to [code]false[/code].
If [code]true[/code], the shader will keep the scale set for the mesh. Otherwise, the scale is lost when billboarding. Only applies when [member billboard_mode] is not [constant BILLBOARD_DISABLED].
@@ -144,6 +147,7 @@
Texture that defines the strength of the clearcoat effect and the glossiness of the clearcoat. Strength is specified in the red channel while glossiness is specified in the green channel.
+ [b]Note:[/b] When assigned in the editor, other textures' properties will automatically be set based on their texture name. For example, if you assign [code]res://levels/wood_clearcoat.png[/code] to [member clearcoat_texture], then [code]res://levels/wood_normal.png[/code] will be assigned to [member normal_texture] and so on. This is based on common texture name conventions, so most texture websites should be handled automatically out of the box. This behavior can be disabled by setting [member EditorSettings.interface/inspector/auto_assign_pbr_material_textures] to [code]false[/code].
Determines which side of the triangle to cull depending on whether the triangle faces towards or away from the camera. See [enum CullMode].
@@ -154,6 +158,7 @@
Texture that specifies the color of the detail overlay. [member detail_albedo]'s alpha channel is used as a mask, even when the material is opaque. To use a dedicated texture as a mask, see [member detail_mask].
[b]Note:[/b] [member detail_albedo] is [i]not[/i] modulated by [member albedo_color].
+ [b]Note:[/b] When assigned in the editor, other textures' properties will automatically be set based on their texture name. For example, if you assign [code]res://levels/wood_detail.png[/code] to [member detail_albedo], then [code]res://levels/wood_normal.png[/code] will be assigned to [member normal_texture] and so on. This is based on common texture name conventions, so most texture websites should be handled automatically out of the box. This behavior can be disabled by setting [member EditorSettings.interface/inspector/auto_assign_pbr_material_textures] to [code]false[/code].
Specifies how the [member detail_albedo] should blend with the current [code]ALBEDO[/code]. See [enum BlendMode] for options.
@@ -214,6 +219,7 @@
Texture that specifies how much surface emits light at a given point.
+ [b]Note:[/b] When assigned in the editor, other textures' properties will automatically be set based on their texture name. For example, if you assign [code]res://levels/wood_emission.png[/code] to [member emission_texture], then [code]res://levels/wood_normal.png[/code] will be assigned to [member normal_texture] and so on. This is based on common texture name conventions, so most texture websites should be handled automatically out of the box. This behavior can be disabled by setting [member EditorSettings.interface/inspector/auto_assign_pbr_material_textures] to [code]false[/code].
If [code]true[/code], the object is rendered at the same size regardless of distance.
@@ -258,6 +264,7 @@
The texture to use as a height map. See also [member heightmap_enabled].
For best results, the texture should be normalized (with [member heightmap_scale] reduced to compensate). In [url=https://gimp.org]GIMP[/url], this can be done using [b]Colors > Auto > Equalize[/b]. If the texture only uses a small part of its available range, the parallax effect may look strange, especially when the camera moves.
[b]Note:[/b] To reduce memory usage and improve loading times, you may be able to use a lower-resolution heightmap texture as most heightmaps are only comprised of low-frequency data.
+ [b]Note:[/b] When assigned in the editor, other textures' properties will automatically be set based on their texture name. For example, if you assign [code]res://levels/wood_heightmap.png[/code] to [member heightmap_texture], then [code]res://levels/wood_normal.png[/code] will be assigned to [member normal_texture] and so on. This is based on common texture name conventions, so most texture websites should be handled automatically out of the box. This behavior can be disabled by setting [member EditorSettings.interface/inspector/auto_assign_pbr_material_textures] to [code]false[/code].
A high value makes the material appear more like a metal. Non-metals use their albedo as the diffuse color and add diffuse to the specular reflection. With non-metals, the reflection appears on top of the albedo color. Metals use their albedo as a multiplier to the specular reflection and set the diffuse color to black resulting in a tinted reflection. Materials work better when fully metal or fully non-metal, values between [code]0[/code] and [code]1[/code] should only be used for blending between metal and non-metal sections. To alter the amount of reflection use [member roughness].
@@ -268,6 +275,7 @@
Texture used to specify metallic for an object. This is multiplied by [member metallic].
+ [b]Note:[/b] When assigned in the editor, other textures' properties will automatically be set based on their texture name. For example, if you assign [code]res://levels/wood_metallic.png[/code] to [member metallic_texture], then [code]res://levels/wood_normal.png[/code] will be assigned to [member normal_texture] and so on. This is based on common texture name conventions, so most texture websites should be handled automatically out of the box. This behavior can be disabled by setting [member EditorSettings.interface/inspector/auto_assign_pbr_material_textures] to [code]false[/code].
Specifies the channel of the [member metallic_texture] in which the metallic information is stored. This is useful when you store the information for multiple effects in a single texture. For example if you stored metallic in the red channel, roughness in the blue, and ambient occlusion in the green you could reduce the number of textures you use.
@@ -292,9 +300,11 @@
[b]Note:[/b] The mesh must have both normals and tangents defined in its vertex data. Otherwise, the normal map won't render correctly and will only appear to darken the whole surface. If creating geometry with [SurfaceTool], you can use [method SurfaceTool.generate_normals] and [method SurfaceTool.generate_tangents] to automatically generate normals and tangents respectively.
[b]Note:[/b] Godot expects the normal map to use X+, Y+, and Z+ coordinates. See [url=http://wiki.polycount.com/wiki/Normal_Map_Technical_Details#Common_Swizzle_Coordinates]this page[/url] for a comparison of normal map coordinates expected by popular engines.
[b]Note:[/b] If [member detail_enabled] is [code]true[/code], the [member detail_albedo] texture is drawn [i]below[/i] the [member normal_texture]. To display a normal map [i]above[/i] the [member detail_albedo] texture, use [member detail_normal] instead.
+ [b]Note:[/b] When assigned in the editor, other textures' properties will automatically be set based on their texture name. For example, if you assign [code]res://levels/wood_normal.png[/code] to [member normal_texture], then [code]res://levels/wood_albedo.png[/code] will be assigned to [member albedo_texture] and so on. This is based on common texture name conventions, so most texture websites should be handled automatically out of the box. This behavior can be disabled by setting [member EditorSettings.interface/inspector/auto_assign_pbr_material_textures] to [code]false[/code].
The Occlusion/Roughness/Metallic texture to use. This is a more efficient replacement of [member ao_texture], [member roughness_texture] and [member metallic_texture] in [ORMMaterial3D]. Ambient occlusion is stored in the red channel. Roughness map is stored in the green channel. Metallic map is stored in the blue channel. The alpha channel is ignored.
+ [b]Note:[/b] When assigned in the editor, other textures' properties will automatically be set based on their texture name. For example, if you assign [code]res://levels/wood_orm.png[/code] to [member orm_texture], then [code]res://levels/wood_normal.png[/code] will be assigned to [member normal_texture] and so on. This is based on common texture name conventions, so most texture websites should be handled automatically out of the box. This behavior can be disabled by setting [member EditorSettings.interface/inspector/auto_assign_pbr_material_textures] to [code]false[/code].
The number of horizontal frames in the particle sprite sheet. Only enabled when using [constant BILLBOARD_PARTICLES]. See [member billboard_mode].
@@ -322,6 +332,7 @@
Texture that controls the strength of the refraction per-pixel. Multiplied by [member refraction_scale].
+ [b]Note:[/b] When assigned in the editor, other textures' properties will automatically be set based on their texture name. For example, if you assign [code]res://levels/wood_refraction.png[/code] to [member refraction_texture], then [code]res://levels/wood_normal.png[/code] will be assigned to [member normal_texture] and so on. This is based on common texture name conventions, so most texture websites should be handled automatically out of the box. This behavior can be disabled by setting [member EditorSettings.interface/inspector/auto_assign_pbr_material_textures] to [code]false[/code].
Specifies the channel of the [member refraction_texture] in which the refraction information is stored. This is useful when you store the information for multiple effects in a single texture. For example if you stored refraction in the red channel, roughness in the blue, and ambient occlusion in the green you could reduce the number of textures you use.
@@ -335,6 +346,7 @@
Texture used to set the strength of the rim lighting effect per-pixel. Multiplied by [member rim].
+ [b]Note:[/b] When assigned in the editor, other textures' properties will automatically be set based on their texture name. For example, if you assign [code]res://levels/wood_rim.png[/code] to [member rim_texture], then [code]res://levels/wood_normal.png[/code] will be assigned to [member normal_texture] and so on. This is based on common texture name conventions, so most texture websites should be handled automatically out of the box. This behavior can be disabled by setting [member EditorSettings.interface/inspector/auto_assign_pbr_material_textures] to [code]false[/code].
The amount of to blend light and albedo color when rendering rim effect. If [code]0[/code] the light color is used, while [code]1[/code] means albedo color is used. An intermediate value generally works best.
@@ -344,6 +356,7 @@
Texture used to control the roughness per-pixel. Multiplied by [member roughness].
+ [b]Note:[/b] When assigned in the editor, other textures' properties will automatically be set based on their texture name. For example, if you assign [code]res://levels/wood_roughness.png[/code] to [member roughness_texture], then [code]res://levels/wood_normal.png[/code] will be assigned to [member normal_texture] and so on. This is based on common texture name conventions, so most texture websites should be handled automatically out of the box. This behavior can be disabled by setting [member EditorSettings.interface/inspector/auto_assign_pbr_material_textures] to [code]false[/code].
Specifies the channel of the [member roughness_texture] in which the roughness information is stored. This is useful when you store the information for multiple effects in a single texture. For example if you stored metallic in the red channel, roughness in the blue, and ambient occlusion in the green you could reduce the number of textures you use.
@@ -369,6 +382,7 @@
Texture used to control the subsurface scattering strength. Stored in the red texture channel. Multiplied by [member subsurf_scatter_strength].
+ [b]Note:[/b] When assigned in the editor, other textures' properties will automatically be set based on their texture name. For example, if you assign [code]res://levels/wood_subsurf.png[/code] to [member subsurf_scatter_texture], then [code]res://levels/wood_normal.png[/code] will be assigned to [member normal_texture] and so on. This is based on common texture name conventions, so most texture websites should be handled automatically out of the box. This behavior can be disabled by setting [member EditorSettings.interface/inspector/auto_assign_pbr_material_textures] to [code]false[/code].
The intensity of the subsurface scattering transmittance effect.
@@ -384,6 +398,7 @@
The texture to use for multiplying the intensity of the subsurface scattering transmittance intensity. See also [member subsurf_scatter_texture]. Ignored if [member subsurf_scatter_skin_mode] is [code]true[/code].
+ [b]Note:[/b] When assigned in the editor, other textures' properties will automatically be set based on their texture name. For example, if you assign [code]res://levels/wood_transmittance.png[/code] to [member subsurf_scatter_transmittance_texture], then [code]res://levels/wood_normal.png[/code] will be assigned to [member normal_texture] and so on. This is based on common texture name conventions, so most texture websites should be handled automatically out of the box. This behavior can be disabled by setting [member EditorSettings.interface/inspector/auto_assign_pbr_material_textures] to [code]false[/code].
Filter flags for the texture. See [enum TextureFilter] for options.
diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml
index ea987da6f5fc..4dfb907a7e18 100644
--- a/doc/classes/EditorSettings.xml
+++ b/doc/classes/EditorSettings.xml
@@ -898,6 +898,9 @@
If [code]true[/code], the Scene dock will display buttons to quickly add a root node to a newly created scene.
+
+ If [code]true[/code], when assigning [BaseMaterial3D] textures in the editor, other textures' properties will automatically be set based on their texture name. For example, if you assign [code]res://levels/wood_albedo.png[/code] to [member BaseMaterial3D.albedo_texture], then [code]res://levels/wood_normal.png[/code] will be assigned to [member BaseMaterial3D.normal_texture] and so on. This is based on common texture name conventions, so most texture websites should be handled automatically out of the box.
+
If [code]true[/code], automatically expands property groups in the Inspector dock when opening a scene that hasn't been opened previously. If [code]false[/code], all groups remain collapsed by default.
diff --git a/editor/editor_property_name_processor.cpp b/editor/editor_property_name_processor.cpp
index ca8854f79702..1d3df1e8e66b 100644
--- a/editor/editor_property_name_processor.cpp
+++ b/editor/editor_property_name_processor.cpp
@@ -254,6 +254,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() {
capitalize_string_remaps["opentype"] = "OpenType";
capitalize_string_remaps["openxr"] = "OpenXR";
capitalize_string_remaps["osslsigncode"] = "osslsigncode";
+ capitalize_string_remaps["pbr"] = "PBR";
capitalize_string_remaps["pck"] = "PCK";
capitalize_string_remaps["png"] = "PNG";
capitalize_string_remaps["po2"] = "(Power of 2)"; // Unit.
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index b0d1c3e6bb1f..07b2abeb16ca 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -525,6 +525,8 @@ void EditorSettings::_load_defaults(Ref p_extra_config) {
open_in_new_inspector_defaults.push_back("MeshLibrary");
_initial_set("interface/inspector/resources_to_open_in_new_inspector", open_in_new_inspector_defaults);
+ _initial_set("interface/inspector/auto_assign_pbr_material_textures", true);
+
EDITOR_SETTING_BASIC(Variant::INT, PROPERTY_HINT_ENUM, "interface/inspector/default_color_picker_mode", (int32_t)ColorPicker::MODE_RGB, "RGB,HSV,RAW,OKHSL")
EDITOR_SETTING_BASIC(Variant::INT, PROPERTY_HINT_ENUM, "interface/inspector/default_color_picker_shape", (int32_t)ColorPicker::SHAPE_OKHSL_CIRCLE, "HSV Rectangle,HSV Rectangle Wheel,VHS Circle,OKHSL Circle")
diff --git a/editor/plugins/material_editor_plugin.cpp b/editor/plugins/material_editor_plugin.cpp
index 8bdc763ebef0..72f40499fcad 100644
--- a/editor/plugins/material_editor_plugin.cpp
+++ b/editor/plugins/material_editor_plugin.cpp
@@ -35,6 +35,7 @@
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
+#include "editor/gui/editor_toaster.h"
#include "editor/themes/editor_scale.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/light_3d.h"
@@ -389,35 +390,224 @@ void EditorInspectorPluginMaterial::_undo_redo_inspector_callback(Object *p_undo
EditorUndoRedoManager *undo_redo = Object::cast_to(p_undo_redo);
ERR_FAIL_NULL(undo_redo);
- // For BaseMaterial3D, if a roughness or metallic textures is being assigned to an empty slot,
- // set the respective metallic or roughness factor to 1.0 as a convenience feature
+ bool orm_material = false;
BaseMaterial3D *base_material = Object::cast_to(p_edited);
- if (base_material) {
- Texture2D *texture = Object::cast_to(p_new_value);
- if (texture) {
- if (p_property == "roughness_texture") {
- if (base_material->get_texture(StandardMaterial3D::TEXTURE_ROUGHNESS).is_null()) {
- undo_redo->add_do_property(p_edited, "roughness", 1.0);
-
- bool valid = false;
- Variant value = p_edited->get("roughness", &valid);
- if (valid) {
- undo_redo->add_undo_property(p_edited, "roughness", value);
+ if (!base_material) {
+ base_material = Object::cast_to(p_edited);
+ if (!base_material) {
+ return;
+ }
+ orm_material = true;
+ }
+
+ Texture2D *texture = Object::cast_to(p_new_value);
+ if (texture) {
+ // Register "do" and "undo" actions automatically, setting the specified property to the specified value.
+#define REGISTER_DO_AND_UNDO(m_property, m_value) \
+ { \
+ undo_redo->add_do_property(p_edited, m_property, m_value); \
+ bool valid = false; \
+ Variant value = p_edited->get(m_property, &valid); \
+ if (valid) { \
+ undo_redo->add_undo_property(p_edited, m_property, value); \
+ } \
+ }
+
+ // For BaseMaterial3D, if a roughness or metallic textures is being assigned to an empty slot,
+ // set the respective metallic or roughness factor to 1.0 as a convenience feature.
+ if (p_property == "roughness_texture") {
+ if (base_material->get_texture(StandardMaterial3D::TEXTURE_ROUGHNESS).is_null()) {
+ REGISTER_DO_AND_UNDO("roughness", 1.0);
+ }
+ } else if (p_property == "metallic_texture") {
+ if (base_material->get_texture(StandardMaterial3D::TEXTURE_METALLIC).is_null()) {
+ REGISTER_DO_AND_UNDO("metallic", 1.0);
+ }
+ }
+
+ if (EDITOR_GET("interface/inspector/auto_assign_pbr_material_textures")) {
+ // Fill in material texture slots based on the specified texture name.
+ const String texture_path = texture->get_path();
+ const Dictionary found_textures = get_material_from_texture_path(texture_path);
+
+ PackedStringArray assigned_textures;
+ if (!found_textures.is_empty()) {
+ if (found_textures.has("albedo")) {
+ REGISTER_DO_AND_UNDO("albedo_texture", ResourceLoader::load(found_textures["albedo"]));
+ assigned_textures.push_back("albedo");
+ }
+
+ if (found_textures.has("normal")) {
+ REGISTER_DO_AND_UNDO("normal_enabled", true);
+ REGISTER_DO_AND_UNDO("normal_texture", ResourceLoader::load(found_textures["normal"]));
+ assigned_textures.push_back("normal");
+ }
+
+ if (found_textures.has("orm")) {
+ REGISTER_DO_AND_UNDO("ao_enabled", true);
+ REGISTER_DO_AND_UNDO("orm_texture", ResourceLoader::load(found_textures["orm"]));
+ if (orm_material) {
+ assigned_textures.push_back("ORM");
+ } else {
+ EditorToaster::get_singleton()->popup_str(vformat(TTR("%s: An ORM texture is available, but this is a StandardMaterial3D. The ORM texture won't be assigned to the material. To resolve this, create an ORMMaterial3D in place of this StandardMaterial3D."), texture_path));
+ }
+ }
+
+ if (found_textures.has("ao")) {
+ REGISTER_DO_AND_UNDO("ao_enabled", true);
+ REGISTER_DO_AND_UNDO("ao_texture", ResourceLoader::load(found_textures["ao"]));
+ if (!orm_material) {
+ assigned_textures.push_back("ambient occlusion");
+ } else {
+ EditorToaster::get_singleton()->popup_str(vformat(TTR("%s: An ambient occlusion texture is available, but this is an ORMMaterial3D. The ambient occlusion texture won't be assigned to the material. To resolve this, create a StandardMaterial3D in place of this ORMMaterial3D."), texture_path));
+ }
+ }
+
+ if (found_textures.has("roughness")) {
+ REGISTER_DO_AND_UNDO("roughness", 1.0);
+ REGISTER_DO_AND_UNDO("roughness_texture", ResourceLoader::load(found_textures["roughness"]));
+ if (!orm_material) {
+ assigned_textures.push_back("roughness");
+ } else {
+ EditorToaster::get_singleton()->popup_str(vformat(TTR("%s: A roughness texture is available, but this is an ORMMaterial3D. The roughness texture won't be assigned to the material. To resolve this, create a StandardMaterial3D in place of this ORMMaterial3D."), texture_path));
}
}
- } else if (p_property == "metallic_texture") {
- if (base_material->get_texture(StandardMaterial3D::TEXTURE_METALLIC).is_null()) {
- undo_redo->add_do_property(p_edited, "metallic", 1.0);
-
- bool valid = false;
- Variant value = p_edited->get("metallic", &valid);
- if (valid) {
- undo_redo->add_undo_property(p_edited, "metallic", value);
+
+ if (found_textures.has("metallic")) {
+ REGISTER_DO_AND_UNDO("metallic", 1.0);
+ REGISTER_DO_AND_UNDO("metallic_texture", ResourceLoader::load(found_textures["metallic"]));
+ if (!orm_material) {
+ assigned_textures.push_back("metallic");
+ } else {
+ EditorToaster::get_singleton()->popup_str(vformat(TTR("%s: A metallic texture is available, but this is an ORMMaterial3D. The metallic texture won't be assigned to the material. To resolve this, create a StandardMaterial3D in place of this ORMMaterial3D."), texture_path));
+ }
+ }
+
+ if (found_textures.has("emission")) {
+ REGISTER_DO_AND_UNDO("emission_enabled", true);
+ REGISTER_DO_AND_UNDO("emission_texture", ResourceLoader::load(found_textures["emission"]));
+ assigned_textures.push_back("emission");
+ }
+
+ if (found_textures.has("heightmap")) {
+ REGISTER_DO_AND_UNDO("heightmap_enabled", true);
+ REGISTER_DO_AND_UNDO("heightmap_texture", ResourceLoader::load(found_textures["heightmap"]));
+ assigned_textures.push_back("heightmap");
+ }
+
+ if (found_textures.has("rim")) {
+ REGISTER_DO_AND_UNDO("rim_enabled", true);
+ REGISTER_DO_AND_UNDO("rim_texture", ResourceLoader::load(found_textures["rim"]));
+ assigned_textures.push_back("rim");
+ }
+
+ if (found_textures.has("clearcoat")) {
+ REGISTER_DO_AND_UNDO("clearcoat_enabled", true);
+ REGISTER_DO_AND_UNDO("clearcoat_texture", ResourceLoader::load(found_textures["clearcoat"]));
+ assigned_textures.push_back("clearcoat");
+ }
+
+ if (found_textures.has("anisotropy")) {
+ REGISTER_DO_AND_UNDO("anisotropy_enabled", true);
+ REGISTER_DO_AND_UNDO("anisotropy_flowmap", ResourceLoader::load(found_textures["anisotropy"]));
+ assigned_textures.push_back("anisotropy");
+ }
+
+ if (found_textures.has("subsurf_scatter")) {
+ REGISTER_DO_AND_UNDO("subsurf_scatter_enabled", true);
+ REGISTER_DO_AND_UNDO("subsurf_scatter_texture", ResourceLoader::load(found_textures["subsurf_scatter"]));
+ assigned_textures.push_back("subsurface scattering");
+ }
+
+ if (found_textures.has("subsurf_scatter_transmittance")) {
+ REGISTER_DO_AND_UNDO("subsurf_scatter_transmittance_enabled", true);
+ REGISTER_DO_AND_UNDO("subsurf_scatter_transmittance_texture", ResourceLoader::load(found_textures["subsurf_scatter_transmittance"]));
+ assigned_textures.push_back("subsurface scattering transmittance");
+ }
+
+ if (found_textures.has("backlight")) {
+ REGISTER_DO_AND_UNDO("backlight_enabled", true);
+ REGISTER_DO_AND_UNDO("backlight_texture", ResourceLoader::load(found_textures["backlight"]));
+ assigned_textures.push_back("backlight");
+ }
+
+ if (found_textures.has("refraction")) {
+ REGISTER_DO_AND_UNDO("refraction_enabled", true);
+ REGISTER_DO_AND_UNDO("refraction_texture", ResourceLoader::load(found_textures["refraction"]));
+ assigned_textures.push_back("refraction");
+ }
+
+ if (found_textures.has("detail_albedo")) {
+ REGISTER_DO_AND_UNDO("detail_enabled", true);
+ REGISTER_DO_AND_UNDO("detail_albedo", ResourceLoader::load(found_textures["detail_albedo"]));
+ assigned_textures.push_back("detail albedo");
+ }
+ }
+
+ if (!assigned_textures.is_empty()) {
+ EditorToaster::get_singleton()->popup_str(vformat(TTR("%s: Automatically assigned %d textures (%s) based on file names."), texture_path, assigned_textures.size(), String(", ").join(assigned_textures)));
+ }
+ }
+ }
+#undef REGISTER_DO_AND_UNDO
+}
+
+Dictionary EditorInspectorPluginMaterial::get_material_from_texture_path(const String &p_file_path) const {
+ if (p_file_path.is_empty()) {
+ // We can't detect the material if the texture is built-in and therefore doesn't have a file name.
+ return Dictionary();
+ }
+
+ String path_type;
+ String found_suffix;
+ // Read from the last component to the first, so that names like `blue_metal_diff`
+ // are seen as an albedo (diffuse) texture instead of a metallic map.
+ PackedStringArray components = p_file_path.get_basename().get_file().replace("-", "_").replace(" ", "_").replace(".", "_").split("_", false);
+ components.reverse();
+
+ for (const String &component : components) {
+ if (found_suffix.is_empty()) {
+ for (const String type : texture_types_from_components.keys()) {
+ for (const String &suffix : PackedStringArray(texture_types_from_components[type])) {
+ // Check PascalCase, lowercase and camelCase.
+ for (const String &suffix_casing : PackedStringArray({ suffix, suffix.to_lower(), suffix.to_camel_case() })) {
+ if (component == suffix_casing) {
+ path_type = type;
+ found_suffix = suffix_casing;
+ break;
+ }
}
}
}
+ } else {
+ break;
+ }
+ }
+
+ if (found_suffix.is_empty()) {
+ // Couldn't detect material based on file name.
+ return Dictionary();
+ }
+
+ Dictionary found_textures;
+ for (const String type : texture_types_from_components.keys()) {
+ if (type == path_type) {
+ // We already know this texture's type, since it was the texture originally specified.
+ found_textures[type] = p_file_path;
+ continue;
+ }
+
+ for (const String &suffix : PackedStringArray(texture_types_from_components[type])) {
+ for (const String &suffix_casing : PackedStringArray({ suffix, suffix.to_lower(), suffix.to_camel_case() })) {
+ const String file_path_casing = p_file_path.replace(found_suffix, suffix_casing);
+ if (FileAccess::exists(file_path_casing)) {
+ found_textures[type] = file_path_casing;
+ }
+ }
}
}
+
+ return found_textures;
}
EditorInspectorPluginMaterial::EditorInspectorPluginMaterial() {
@@ -428,6 +618,26 @@ EditorInspectorPluginMaterial::EditorInspectorPluginMaterial() {
env->set_ambient_source(Environment::AMBIENT_SOURCE_SKY);
env->set_reflection_source(Environment::REFLECTION_SOURCE_SKY);
+ // Common PBR material texture types, standardized across engines.
+ texture_types_from_components["albedo"] = PackedStringArray({ "Albedo", "BaseColor", "BaseColour", "Base", "Color", "Colour", "Diffuse", "Diff", "C", "D" });
+ texture_types_from_components["normal"] = PackedStringArray({ "Normal", "NormalMap", "NormalGL", "NormalDX", "Local", "Norm", "Nor", "Nor_GL", "Nor_DX", "NM", "N" });
+ texture_types_from_components["orm"] = PackedStringArray({ "ORM", "ARM" });
+ texture_types_from_components["ao"] = PackedStringArray({ "AO", "AmbientOcclusion", "Ambient", "Occlusion", "A", "O" });
+ texture_types_from_components["roughness"] = PackedStringArray({ "Roughness", "Rough", "R" });
+ texture_types_from_components["metallic"] = PackedStringArray({ "Metallic", "Metalness", "Metal", "M" });
+ texture_types_from_components["emission"] = PackedStringArray({ "Emission", "Emissive", "Glow", "Luma", "E", "G" });
+ texture_types_from_components["heightmap"] = PackedStringArray({ "Height", "HeightMap", "Displacement", "Disp", "H", "Z" });
+
+ // Less common and not as standardized across engines.
+ texture_types_from_components["rim"] = PackedStringArray({ "Rim" });
+ texture_types_from_components["clearcoat"] = PackedStringArray({ "Clearcoat" });
+ texture_types_from_components["anisotropy"] = PackedStringArray({ "Anisotropy", "Aniso", "Flowmap", "Flow" });
+ texture_types_from_components["subsurf_scatter"] = PackedStringArray({ "Subsurface", "Subsurf", "Scattering", "Scatter", "SSS" });
+ texture_types_from_components["subsurf_scatter_transmittance"] = PackedStringArray({ "Transmittance", "Transmission", "Transmissive" });
+ texture_types_from_components["backlight"] = PackedStringArray({ "BackLighting", "Backlight" });
+ texture_types_from_components["refraction"] = PackedStringArray({ "Refraction", "Refract" });
+ texture_types_from_components["detail_albedo"] = PackedStringArray({ "Detail" });
+
EditorNode::get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &EditorInspectorPluginMaterial::_undo_redo_inspector_callback));
}
diff --git a/editor/plugins/material_editor_plugin.h b/editor/plugins/material_editor_plugin.h
index c1b37a5831b0..3915a7f58f45 100644
--- a/editor/plugins/material_editor_plugin.h
+++ b/editor/plugins/material_editor_plugin.h
@@ -118,12 +118,30 @@ class EditorInspectorPluginMaterial : public EditorInspectorPlugin {
GDCLASS(EditorInspectorPluginMaterial, EditorInspectorPlugin);
Ref env;
+ // Values must be written in PascalCase.
+ // The file name is stripped from its extension, then each component (each word separated by `-`, `_` or `.`)
+ // is checked individually for a match. Each component will check for the original casing,
+ // convert to lowercase, then convert the first character to lowercase. This covers the PascalCase,
+ // lowercase and camelCase file naming conventions.
+ //
+ // The order of the keys is important, since we want some material maps to be checked before others (e.g. albedo takes
+ // priority over metallic, so that `metal_grate_albedo.png` is correctly detected as an albedo map).
+ //
+ // Websites used to determine common file names:
+ //
+ // - https://polyhaven.com/
+ // - http://cgbookcase.com/
+ // - https://ambientcg.com/
+ Dictionary texture_types_from_components;
+
public:
virtual bool can_handle(Object *p_object) override;
virtual void parse_begin(Object *p_object) override;
void _undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, const String &p_property, const Variant &p_new_value);
+ Dictionary get_material_from_texture_path(const String &p_file_path) const;
+
EditorInspectorPluginMaterial();
};