diff --git a/doc/classes/GeometryInstance.xml b/doc/classes/GeometryInstance.xml
index af2804f7d58..b47af6bc804 100644
--- a/doc/classes/GeometryInstance.xml
+++ b/doc/classes/GeometryInstance.xml
@@ -61,6 +61,10 @@
The GeometryInstance's min LOD margin.
[b]Note:[/b] This property currently has no effect.
+
+ The material overlay for the whole geometry.
+ If a material is assigned to this property, it will be rendered on top of any other active material for all the surfaces.
+
The material override for the whole geometry.
If a material is assigned to this property, it will be used instead of any material set in any material slot of the mesh.
diff --git a/doc/classes/VisualServer.xml b/doc/classes/VisualServer.xml
index 4ceb9d34f61..4d80d316888 100644
--- a/doc/classes/VisualServer.xml
+++ b/doc/classes/VisualServer.xml
@@ -1374,6 +1374,14 @@
Sets the flag for a given [enum InstanceFlags]. See [enum InstanceFlags] for more details.
+
+
+
+
+
+ Sets a material that will be rendered for all surfaces on top of active materials for the mesh associated with this instance. Equivalent to [member GeometryInstance.material_overlay].
+
+
diff --git a/drivers/gles2/rasterizer_scene_gles2.cpp b/drivers/gles2/rasterizer_scene_gles2.cpp
index 31362045e2b..2bd4bab0f65 100644
--- a/drivers/gles2/rasterizer_scene_gles2.cpp
+++ b/drivers/gles2/rasterizer_scene_gles2.cpp
@@ -979,6 +979,27 @@ void RasterizerSceneGLES2::_add_geometry(RasterizerStorageGLES2::Geometry *p_geo
_add_geometry_with_material(p_geometry, p_instance, p_owner, material, p_depth_pass, p_shadow_pass);
}
+
+ // Repeat the "nested chain" logic also for the overlay
+ if (p_instance->material_overlay.is_valid()) {
+ material = storage->material_owner.getornull(p_instance->material_overlay);
+
+ if (!material || !material->shader || !material->shader->valid) {
+ return;
+ }
+
+ _add_geometry_with_material(p_geometry, p_instance, p_owner, material, p_depth_pass, p_shadow_pass);
+
+ while (material->next_pass.is_valid()) {
+ material = storage->material_owner.getornull(material->next_pass);
+
+ if (!material || !material->shader || !material->shader->valid) {
+ break;
+ }
+
+ _add_geometry_with_material(p_geometry, p_instance, p_owner, material, p_depth_pass, p_shadow_pass);
+ }
+ }
}
void RasterizerSceneGLES2::_add_geometry_with_material(RasterizerStorageGLES2::Geometry *p_geometry, InstanceBase *p_instance, RasterizerStorageGLES2::GeometryOwner *p_owner, RasterizerStorageGLES2::Material *p_material, bool p_depth_pass, bool p_shadow_pass) {
bool has_base_alpha = (p_material->shader->spatial.uses_alpha && !p_material->shader->spatial.uses_alpha_scissor) || p_material->shader->spatial.uses_screen_texture || p_material->shader->spatial.uses_depth_texture;
diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp
index 1a6efc83e20..4eecdd0b31d 100644
--- a/drivers/gles2/rasterizer_storage_gles2.cpp
+++ b/drivers/gles2/rasterizer_storage_gles2.cpp
@@ -5899,6 +5899,10 @@ bool RasterizerStorageGLES2::free(RID p_rid) {
ins->material_override = RID();
}
+ if (ins->material_overlay == p_rid) {
+ ins->material_overlay = RID();
+ }
+
for (int i = 0; i < ins->materials.size(); i++) {
if (ins->materials[i] == p_rid) {
ins->materials.write[i] = RID();
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index dfa25f045ed..892259e0493 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -2222,6 +2222,27 @@ void RasterizerSceneGLES3::_add_geometry(RasterizerStorageGLES3::Geometry *p_geo
}
_add_geometry_with_material(p_geometry, p_instance, p_owner, m, p_depth_pass, p_shadow_pass);
}
+
+ // Repeat the "nested chain" logic also for the overlay
+ if (p_instance->material_overlay.is_valid()) {
+ m = storage->material_owner.getornull(p_instance->material_overlay);
+
+ if (!m || !m->shader || !m->shader->valid) {
+ return;
+ }
+
+ _add_geometry_with_material(p_geometry, p_instance, p_owner, m, p_depth_pass, p_shadow_pass);
+
+ while (m->next_pass.is_valid()) {
+ m = storage->material_owner.getornull(m->next_pass);
+
+ if (!m || !m->shader || !m->shader->valid) {
+ break;
+ }
+
+ _add_geometry_with_material(p_geometry, p_instance, p_owner, m, p_depth_pass, p_shadow_pass);
+ }
+ }
}
void RasterizerSceneGLES3::_add_geometry_with_material(RasterizerStorageGLES3::Geometry *p_geometry, InstanceBase *p_instance, RasterizerStorageGLES3::GeometryOwner *p_owner, RasterizerStorageGLES3::Material *p_material, bool p_depth_pass, bool p_shadow_pass) {
diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp
index 20cea5a1459..586eb7f3969 100644
--- a/drivers/gles3/rasterizer_storage_gles3.cpp
+++ b/drivers/gles3/rasterizer_storage_gles3.cpp
@@ -7784,10 +7784,15 @@ bool RasterizerStorageGLES3::free(RID p_rid) {
}
for (Map::Element *E = material->instance_owners.front(); E; E = E->next()) {
RasterizerScene::InstanceBase *ins = E->key();
+
if (ins->material_override == p_rid) {
ins->material_override = RID();
}
+ if (ins->material_overlay == p_rid) {
+ ins->material_overlay = RID();
+ }
+
for (int i = 0; i < ins->materials.size(); i++) {
if (ins->materials[i] == p_rid) {
ins->materials.write[i] = RID();
diff --git a/scene/3d/mesh_instance.cpp b/scene/3d/mesh_instance.cpp
index 11b101acbad..93e4f1792e6 100644
--- a/scene/3d/mesh_instance.cpp
+++ b/scene/3d/mesh_instance.cpp
@@ -733,6 +733,14 @@ void MeshInstance::set_material_override(const Ref &p_material) {
}
}
+void MeshInstance::set_material_overlay(const Ref &p_material) {
+ if (p_material == get_material_overlay()) {
+ return;
+ }
+
+ GeometryInstance::set_material_overlay(p_material);
+}
+
void MeshInstance::set_software_skinning_transform_normals(bool p_enabled) {
if (p_enabled == is_software_skinning_transform_normals_enabled()) {
return;
@@ -852,6 +860,11 @@ bool MeshInstance::is_mergeable_with(const MeshInstance &p_other) {
return false;
}
+ // overlay materials must match
+ if (get_material_overlay() != p_other.get_material_overlay()) {
+ return false;
+ }
+
for (int n = 0; n < num_surfaces; n++) {
// materials must match
if (get_active_material(n) != p_other.get_active_material(n)) {
@@ -1154,6 +1167,9 @@ bool MeshInstance::create_by_merging(Vector p_list) {
set_surface_material(n, first->get_active_material(n));
}
+ // set overlay material
+ set_material_overlay(first->get_material_overlay());
+
return true;
}
diff --git a/scene/3d/mesh_instance.h b/scene/3d/mesh_instance.h
index 52ab39601ce..8880d619016 100644
--- a/scene/3d/mesh_instance.h
+++ b/scene/3d/mesh_instance.h
@@ -127,6 +127,8 @@ public:
virtual void set_material_override(const Ref &p_material);
+ virtual void set_material_overlay(const Ref &p_material);
+
void set_software_skinning_transform_normals(bool p_enabled);
bool is_software_skinning_transform_normals_enabled() const;
diff --git a/scene/3d/visual_instance.cpp b/scene/3d/visual_instance.cpp
index 7e51fe70751..09dc355a84e 100644
--- a/scene/3d/visual_instance.cpp
+++ b/scene/3d/visual_instance.cpp
@@ -175,6 +175,15 @@ Ref GeometryInstance::get_material_override() const {
return material_override;
}
+void GeometryInstance::set_material_overlay(const Ref &p_material) {
+ material_overlay = p_material;
+ VS::get_singleton()->instance_geometry_set_material_overlay(get_instance(), p_material.is_valid() ? p_material->get_rid() : RID());
+}
+
+Ref GeometryInstance::get_material_overlay() const {
+ return material_overlay;
+}
+
void GeometryInstance::set_generate_lightmap(bool p_enabled) {
generate_lightmap = p_enabled;
}
@@ -275,6 +284,9 @@ void GeometryInstance::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_material_override", "material"), &GeometryInstance::set_material_override);
ClassDB::bind_method(D_METHOD("get_material_override"), &GeometryInstance::get_material_override);
+ ClassDB::bind_method(D_METHOD("set_material_overlay", "material"), &GeometryInstance::set_material_overlay);
+ ClassDB::bind_method(D_METHOD("get_material_overlay"), &GeometryInstance::get_material_overlay);
+
ClassDB::bind_method(D_METHOD("set_flag", "flag", "value"), &GeometryInstance::set_flag);
ClassDB::bind_method(D_METHOD("get_flag", "flag"), &GeometryInstance::get_flag);
@@ -308,6 +320,7 @@ void GeometryInstance::_bind_methods() {
ADD_GROUP("Geometry", "");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material_override", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,SpatialMaterial"), "set_material_override", "get_material_override");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material_overlay", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,SpatialMaterial"), "set_material_overlay", "get_material_overlay");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cast_shadow", PROPERTY_HINT_ENUM, "Off,On,Double-Sided,Shadows Only"), "set_cast_shadows_setting", "get_cast_shadows_setting");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "extra_cull_margin", PROPERTY_HINT_RANGE, "0,16384,0.01"), "set_extra_cull_margin", "get_extra_cull_margin");
diff --git a/scene/3d/visual_instance.h b/scene/3d/visual_instance.h
index 6487fc199e0..faa61d4bed9 100644
--- a/scene/3d/visual_instance.h
+++ b/scene/3d/visual_instance.h
@@ -110,6 +110,7 @@ private:
LightmapScale lightmap_scale;
ShadowCastingSetting shadow_casting_setting;
Ref material_override;
+ Ref material_overlay;
float lod_min_distance;
float lod_max_distance;
float lod_min_hysteresis;
@@ -152,6 +153,9 @@ public:
virtual void set_material_override(const Ref &p_material);
Ref get_material_override() const;
+ virtual void set_material_overlay(const Ref &p_material);
+ Ref get_material_overlay() const;
+
void set_extra_cull_margin(float p_margin);
float get_extra_cull_margin() const;
diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h
index 452662c2759..26c2b35b082 100644
--- a/servers/visual/rasterizer.h
+++ b/servers/visual/rasterizer.h
@@ -88,6 +88,7 @@ public:
RID skeleton;
RID material_override;
+ RID material_overlay;
Transform transform;
diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h
index 9e7c754206a..94bd254776e 100644
--- a/servers/visual/visual_server_raster.h
+++ b/servers/visual/visual_server_raster.h
@@ -618,6 +618,7 @@ public:
BIND3(instance_geometry_set_flag, RID, InstanceFlags, bool)
BIND2(instance_geometry_set_cast_shadows_setting, RID, ShadowCastingSetting)
BIND2(instance_geometry_set_material_override, RID, RID)
+ BIND2(instance_geometry_set_material_overlay, RID, RID)
BIND5(instance_geometry_set_draw_range, RID, float, float, float, float)
BIND2(instance_geometry_set_as_instance_lod, RID, RID)
diff --git a/servers/visual/visual_server_scene.cpp b/servers/visual/visual_server_scene.cpp
index f80814b89cf..2f4e76cbeeb 100644
--- a/servers/visual/visual_server_scene.cpp
+++ b/servers/visual/visual_server_scene.cpp
@@ -1520,6 +1520,20 @@ void VisualServerScene::instance_geometry_set_material_override(RID p_instance,
VSG::storage->material_add_instance_owner(instance->material_override, instance);
}
}
+void VisualServerScene::instance_geometry_set_material_overlay(RID p_instance, RID p_material) {
+ Instance *instance = instance_owner.get(p_instance);
+ ERR_FAIL_COND(!instance);
+
+ if (instance->material_overlay.is_valid()) {
+ VSG::storage->material_remove_instance_owner(instance->material_overlay, instance);
+ }
+ instance->material_overlay = p_material;
+ instance->base_changed(false, true);
+
+ if (instance->material_overlay.is_valid()) {
+ VSG::storage->material_add_instance_owner(instance->material_overlay, instance);
+ }
+}
void VisualServerScene::instance_geometry_set_draw_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin) {
}
@@ -3988,6 +4002,11 @@ void VisualServerScene::_update_dirty_instance(Instance *p_instance) {
}
}
+ if (p_instance->material_overlay.is_valid()) {
+ can_cast_shadows = can_cast_shadows || VSG::storage->material_casts_shadows(p_instance->material_overlay);
+ is_animated = is_animated || VSG::storage->material_is_animated(p_instance->material_overlay);
+ }
+
if (can_cast_shadows != geom->can_cast_shadows) {
//ability to cast shadows change, let lights now
for (List::Element *E = geom->lighting.front(); E; E = E->next()) {
@@ -4057,6 +4076,7 @@ bool VisualServerScene::free(RID p_rid) {
instance_set_scenario(p_rid, RID());
instance_set_base(p_rid, RID());
instance_geometry_set_material_override(p_rid, RID());
+ instance_geometry_set_material_overlay(p_rid, RID());
instance_attach_skeleton(p_rid, RID());
update_dirty_instances(); //in case something changed this
diff --git a/servers/visual/visual_server_scene.h b/servers/visual/visual_server_scene.h
index ca5b2285dc1..dd40e4d129f 100644
--- a/servers/visual/visual_server_scene.h
+++ b/servers/visual/visual_server_scene.h
@@ -690,6 +690,7 @@ public:
virtual void instance_geometry_set_flag(RID p_instance, VS::InstanceFlags p_flags, bool p_enabled);
virtual void instance_geometry_set_cast_shadows_setting(RID p_instance, VS::ShadowCastingSetting p_shadow_casting_setting);
virtual void instance_geometry_set_material_override(RID p_instance, RID p_material);
+ virtual void instance_geometry_set_material_overlay(RID p_instance, RID p_material);
virtual void instance_geometry_set_draw_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin);
virtual void instance_geometry_set_as_instance_lod(RID p_instance, RID p_as_lod_of_instance);
diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h
index fe252639bb6..cae62b0d2b2 100644
--- a/servers/visual/visual_server_wrap_mt.h
+++ b/servers/visual/visual_server_wrap_mt.h
@@ -541,6 +541,7 @@ public:
FUNC3(instance_geometry_set_flag, RID, InstanceFlags, bool)
FUNC2(instance_geometry_set_cast_shadows_setting, RID, ShadowCastingSetting)
FUNC2(instance_geometry_set_material_override, RID, RID)
+ FUNC2(instance_geometry_set_material_overlay, RID, RID)
FUNC5(instance_geometry_set_draw_range, RID, float, float, float, float)
FUNC2(instance_geometry_set_as_instance_lod, RID, RID)
diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp
index 45e9bd55c61..71d1022a67a 100644
--- a/servers/visual_server.cpp
+++ b/servers/visual_server.cpp
@@ -2144,6 +2144,7 @@ void VisualServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("instance_geometry_set_flag", "instance", "flag", "enabled"), &VisualServer::instance_geometry_set_flag);
ClassDB::bind_method(D_METHOD("instance_geometry_set_cast_shadows_setting", "instance", "shadow_casting_setting"), &VisualServer::instance_geometry_set_cast_shadows_setting);
ClassDB::bind_method(D_METHOD("instance_geometry_set_material_override", "instance", "material"), &VisualServer::instance_geometry_set_material_override);
+ ClassDB::bind_method(D_METHOD("instance_geometry_set_material_overlay", "instance", "material"), &VisualServer::instance_geometry_set_material_overlay);
ClassDB::bind_method(D_METHOD("instance_geometry_set_draw_range", "instance", "min", "max", "min_margin", "max_margin"), &VisualServer::instance_geometry_set_draw_range);
ClassDB::bind_method(D_METHOD("instance_geometry_set_as_instance_lod", "instance", "as_lod_of_instance"), &VisualServer::instance_geometry_set_as_instance_lod);
diff --git a/servers/visual_server.h b/servers/visual_server.h
index b6da7a91369..65fc1e8e380 100644
--- a/servers/visual_server.h
+++ b/servers/visual_server.h
@@ -961,6 +961,7 @@ public:
virtual void instance_geometry_set_flag(RID p_instance, InstanceFlags p_flags, bool p_enabled) = 0;
virtual void instance_geometry_set_cast_shadows_setting(RID p_instance, ShadowCastingSetting p_shadow_casting_setting) = 0;
virtual void instance_geometry_set_material_override(RID p_instance, RID p_material) = 0;
+ virtual void instance_geometry_set_material_overlay(RID p_instance, RID p_material) = 0;
virtual void instance_geometry_set_draw_range(RID p_instance, float p_min, float p_max, float p_min_margin, float p_max_margin) = 0;
virtual void instance_geometry_set_as_instance_lod(RID p_instance, RID p_as_lod_of_instance) = 0;