Added material_overlay property to MeshInstance

Applying overlay materials into multi-surface meshes currently
requires adding a next pass material to all the surfaces, which
might be cumbersome when the material is to be applied to a range
of different geometries. This also makes it not trivial to use
AnimationPlayer to control the material in case of visual effects.
The material_override property is not an option as it works
replacing the active material for the surfaces, not adding a new pass.

This commit adds the material_overlay property to GeometryInstance
(and therefore MeshInstance), having the same reach as
material_override (that is, all surfaces) but adding a new material
pass on top of the active materials, instead of replacing them.

Implemented in rasterizer of both GLES2 and GLES3.
This commit is contained in:
Fernando Cosentino 2021-07-24 21:45:51 +01:00
parent 6d58ea6ce7
commit b7d17f306f
17 changed files with 127 additions and 0 deletions

View file

@ -68,6 +68,10 @@
The GeometryInstance's min LOD margin.
[b]Note:[/b] This property currently has no effect.
</member>
<member name="material_overlay" type="Material" setter="set_material_overlay" getter="get_material_overlay">
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.
</member>
<member name="material_override" type="Material" setter="set_material_override" getter="get_material_override">
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.

View file

@ -1922,6 +1922,17 @@
Sets the flag for a given [enum InstanceFlags]. See [enum InstanceFlags] for more details.
</description>
</method>
<method name="instance_geometry_set_material_overlay">
<return type="void">
</return>
<argument index="0" name="instance" type="RID">
</argument>
<argument index="1" name="material" type="RID">
</argument>
<description>
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].
</description>
</method>
<method name="instance_geometry_set_material_override">
<return type="void">
</return>

View file

@ -978,6 +978,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;

View file

@ -5630,6 +5630,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();

View file

@ -2207,6 +2207,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) {

View file

@ -7723,10 +7723,15 @@ bool RasterizerStorageGLES3::free(RID p_rid) {
}
for (Map<RasterizerScene::InstanceBase *, int>::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();

View file

@ -715,6 +715,14 @@ void MeshInstance::set_material_override(const Ref<Material> &p_material) {
}
}
void MeshInstance::set_material_overlay(const Ref<Material> &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;
@ -834,6 +842,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)) {
@ -1136,6 +1149,9 @@ bool MeshInstance::create_by_merging(Vector<MeshInstance *> p_list) {
set_surface_material(n, first->get_active_material(n));
}
// set overlay material
set_material_overlay(first->get_material_overlay());
return true;
}

View file

@ -127,6 +127,8 @@ public:
virtual void set_material_override(const Ref<Material> &p_material);
virtual void set_material_overlay(const Ref<Material> &p_material);
void set_software_skinning_transform_normals(bool p_enabled);
bool is_software_skinning_transform_normals_enabled() const;

View file

@ -175,6 +175,15 @@ Ref<Material> GeometryInstance::get_material_override() const {
return material_override;
}
void GeometryInstance::set_material_overlay(const Ref<Material> &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<Material> 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");

View file

@ -110,6 +110,7 @@ private:
LightmapScale lightmap_scale;
ShadowCastingSetting shadow_casting_setting;
Ref<Material> material_override;
Ref<Material> 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<Material> &p_material);
Ref<Material> get_material_override() const;
virtual void set_material_overlay(const Ref<Material> &p_material);
Ref<Material> get_material_overlay() const;
void set_extra_cull_margin(float p_margin);
float get_extra_cull_margin() const;

View file

@ -88,6 +88,7 @@ public:
RID skeleton;
RID material_override;
RID material_overlay;
Transform transform;

View file

@ -604,6 +604,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)

View file

@ -1449,6 +1449,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) {
}
@ -3913,6 +3927,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<Instance *>::Element *E = geom->lighting.front(); E; E = E->next()) {
@ -3982,6 +4001,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

View file

@ -662,6 +662,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);

View file

@ -527,6 +527,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)

View file

@ -1842,6 +1842,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);

View file

@ -932,6 +932,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;