Browse Source

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.
Fernando Cosentino 4 years ago
parent
commit
cc8846bef6

+ 4 - 0
doc/classes/GeometryInstance.xml

@@ -61,6 +61,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.

+ 8 - 0
doc/classes/VisualServer.xml

@@ -1374,6 +1374,14 @@
 				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" />
+			<argument index="0" name="instance" type="RID" />
+			<argument index="1" name="material" type="RID" />
+			<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" />
 			<argument index="0" name="instance" type="RID" />

+ 21 - 0
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;

+ 4 - 0
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();

+ 21 - 0
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) {

+ 5 - 0
drivers/gles3/rasterizer_storage_gles3.cpp

@@ -7784,10 +7784,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();

+ 16 - 0
scene/3d/mesh_instance.cpp

@@ -733,6 +733,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;
@@ -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<MeshInstance *> p_list) {
 		set_surface_material(n, first->get_active_material(n));
 	}
 
+	// set overlay material
+	set_material_overlay(first->get_material_overlay());
+
 	return true;
 }
 

+ 2 - 0
scene/3d/mesh_instance.h

@@ -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;
 

+ 13 - 0
scene/3d/visual_instance.cpp

@@ -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");
 

+ 4 - 0
scene/3d/visual_instance.h

@@ -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;
 

+ 1 - 0
servers/visual/rasterizer.h

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

+ 1 - 0
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)

+ 20 - 0
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<Instance *>::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

+ 1 - 0
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);

+ 1 - 0
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)

+ 1 - 0
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);
 

+ 1 - 0
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;