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;