Browse Source

Add support for projectors in spot and omni lights.

Juan Linietsky 5 years ago
parent
commit
6f293ed795

+ 41 - 1
scene/3d/light_3d.cpp

@@ -70,7 +70,7 @@ void Light3D::set_shadow(bool p_enable) {
 	shadow = p_enable;
 	shadow = p_enable;
 	RS::get_singleton()->light_set_shadow(light, p_enable);
 	RS::get_singleton()->light_set_shadow(light, p_enable);
 
 
-	if (type == RenderingServer::LIGHT_SPOT) {
+	if (type == RenderingServer::LIGHT_SPOT || type == RenderingServer::LIGHT_OMNI) {
 		update_configuration_warning();
 		update_configuration_warning();
 	}
 	}
 }
 }
@@ -166,6 +166,18 @@ Light3D::BakeMode Light3D::get_bake_mode() const {
 	return bake_mode;
 	return bake_mode;
 }
 }
 
 
+void Light3D::set_projector(const Ref<Texture> &p_texture) {
+
+	projector = p_texture;
+	RID tex_id = projector.is_valid() ? projector->get_rid() : RID();
+	RS::get_singleton()->light_set_projector(light, tex_id);
+	update_configuration_warning();
+}
+
+Ref<Texture2D> Light3D::get_projector() const {
+	return projector;
+}
+
 void Light3D::_update_visibility() {
 void Light3D::_update_visibility() {
 
 
 	if (!is_inside_tree())
 	if (!is_inside_tree())
@@ -221,6 +233,10 @@ void Light3D::_validate_property(PropertyInfo &property) const {
 		property.usage = 0;
 		property.usage = 0;
 	}
 	}
 
 
+	if (get_light_type() == RS::LIGHT_DIRECTIONAL && property.name == "light_projector") {
+		property.usage = 0;
+	}
+
 	if (get_light_type() != RS::LIGHT_DIRECTIONAL && property.name == "light_angular_distance") {
 	if (get_light_type() != RS::LIGHT_DIRECTIONAL && property.name == "light_angular_distance") {
 		property.usage = 0;
 		property.usage = 0;
 	}
 	}
@@ -255,10 +271,14 @@ void Light3D::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_bake_mode", "bake_mode"), &Light3D::set_bake_mode);
 	ClassDB::bind_method(D_METHOD("set_bake_mode", "bake_mode"), &Light3D::set_bake_mode);
 	ClassDB::bind_method(D_METHOD("get_bake_mode"), &Light3D::get_bake_mode);
 	ClassDB::bind_method(D_METHOD("get_bake_mode"), &Light3D::get_bake_mode);
 
 
+	ClassDB::bind_method(D_METHOD("set_projector", "projector"), &Light3D::set_projector);
+	ClassDB::bind_method(D_METHOD("get_projector"), &Light3D::get_projector);
+
 	ADD_GROUP("Light", "light_");
 	ADD_GROUP("Light", "light_");
 	ADD_PROPERTY(PropertyInfo(Variant::COLOR, "light_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_color", "get_color");
 	ADD_PROPERTY(PropertyInfo(Variant::COLOR, "light_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_color", "get_color");
 	ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_param", "get_param", PARAM_ENERGY);
 	ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_param", "get_param", PARAM_ENERGY);
 	ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_indirect_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_param", "get_param", PARAM_INDIRECT_ENERGY);
 	ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_indirect_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_param", "get_param", PARAM_INDIRECT_ENERGY);
+	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_projector", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_projector", "get_projector");
 	ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_size", PROPERTY_HINT_RANGE, "0,64,0.01,or_greater"), "set_param", "get_param", PARAM_SIZE);
 	ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_size", PROPERTY_HINT_RANGE, "0,64,0.01,or_greater"), "set_param", "get_param", PARAM_SIZE);
 	ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_angular_distance", PROPERTY_HINT_RANGE, "0,90,0.01"), "set_param", "get_param", PARAM_SIZE);
 	ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_angular_distance", PROPERTY_HINT_RANGE, "0,90,0.01"), "set_param", "get_param", PARAM_SIZE);
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "light_negative"), "set_negative", "is_negative");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "light_negative"), "set_negative", "is_negative");
@@ -444,6 +464,19 @@ OmniLight3D::ShadowMode OmniLight3D::get_shadow_mode() const {
 	return shadow_mode;
 	return shadow_mode;
 }
 }
 
 
+String OmniLight3D::get_configuration_warning() const {
+	String warning = Light3D::get_configuration_warning();
+
+	if (!has_shadow() && get_projector().is_valid()) {
+		if (warning != String()) {
+			warning += "\n\n";
+		}
+		warning += TTR("Projector texture only works with shadows active.");
+	}
+
+	return warning;
+}
+
 void OmniLight3D::_bind_methods() {
 void OmniLight3D::_bind_methods() {
 
 
 	ClassDB::bind_method(D_METHOD("set_shadow_mode", "mode"), &OmniLight3D::set_shadow_mode);
 	ClassDB::bind_method(D_METHOD("set_shadow_mode", "mode"), &OmniLight3D::set_shadow_mode);
@@ -475,6 +508,13 @@ String SpotLight3D::get_configuration_warning() const {
 		warning += TTR("A SpotLight3D with an angle wider than 90 degrees cannot cast shadows.");
 		warning += TTR("A SpotLight3D with an angle wider than 90 degrees cannot cast shadows.");
 	}
 	}
 
 
+	if (!has_shadow() && get_projector().is_valid()) {
+		if (warning != String()) {
+			warning += "\n\n";
+		}
+		warning += TTR("Projector texture only works with shadows active.");
+	}
+
 	return warning;
 	return warning;
 }
 }
 
 

+ 6 - 0
scene/3d/light_3d.h

@@ -81,6 +81,7 @@ private:
 	bool editor_only;
 	bool editor_only;
 	void _update_visibility();
 	void _update_visibility();
 	BakeMode bake_mode;
 	BakeMode bake_mode;
+	Ref<Texture2D> projector;
 
 
 	// bind helpers
 	// bind helpers
 
 
@@ -125,6 +126,9 @@ public:
 	void set_bake_mode(BakeMode p_mode);
 	void set_bake_mode(BakeMode p_mode);
 	BakeMode get_bake_mode() const;
 	BakeMode get_bake_mode() const;
 
 
+	void set_projector(const Ref<Texture> &p_texture);
+	Ref<Texture2D> get_projector() const;
+
 	virtual AABB get_aabb() const;
 	virtual AABB get_aabb() const;
 	virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const;
 	virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const;
 
 
@@ -196,6 +200,8 @@ public:
 	void set_shadow_mode(ShadowMode p_mode);
 	void set_shadow_mode(ShadowMode p_mode);
 	ShadowMode get_shadow_mode() const;
 	ShadowMode get_shadow_mode() const;
 
 
+	virtual String get_configuration_warning() const;
+
 	OmniLight3D();
 	OmniLight3D();
 };
 };
 
 

+ 2 - 2
servers/rendering/rasterizer.h

@@ -327,8 +327,8 @@ public:
 
 
 	virtual Size2 texture_size_with_proxy(RID p_proxy) = 0;
 	virtual Size2 texture_size_with_proxy(RID p_proxy) = 0;
 
 
-	virtual void texture_add_to_decal_atlas(RID p_texture) = 0;
-	virtual void texture_remove_from_decal_atlas(RID p_texture) = 0;
+	virtual void texture_add_to_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) = 0;
+	virtual void texture_remove_from_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) = 0;
 
 
 	/* SHADER API */
 	/* SHADER API */
 
 

+ 3 - 2
servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp

@@ -204,7 +204,7 @@ RID RasterizerEffectsRD::_get_compute_uniform_set_from_image_pair(RID p_texture1
 	return uniform_set;
 	return uniform_set;
 }
 }
 
 
-void RasterizerEffectsRD::copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y) {
+void RasterizerEffectsRD::copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y, bool p_panorama) {
 
 
 	zeromem(&copy_to_fb.push_constant, sizeof(CopyToFbPushConstant));
 	zeromem(&copy_to_fb.push_constant, sizeof(CopyToFbPushConstant));
 
 
@@ -219,7 +219,7 @@ void RasterizerEffectsRD::copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_f
 	}
 	}
 
 
 	RD::DrawListID draw_list = p_draw_list;
 	RD::DrawListID draw_list = p_draw_list;
-	RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, copy_to_fb.pipelines[COPY_TO_FB_COPY].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
+	RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, copy_to_fb.pipelines[p_panorama ? COPY_TO_FB_COPY_PANORAMA_TO_DP : COPY_TO_FB_COPY].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_rd_texture), 0);
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_rd_texture), 0);
 	RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
 	RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
 	RD::get_singleton()->draw_list_set_push_constant(draw_list, &copy_to_fb.push_constant, sizeof(CopyToFbPushConstant));
 	RD::get_singleton()->draw_list_set_push_constant(draw_list, &copy_to_fb.push_constant, sizeof(CopyToFbPushConstant));
@@ -1238,6 +1238,7 @@ RasterizerEffectsRD::RasterizerEffectsRD() {
 	{
 	{
 		Vector<String> copy_modes;
 		Vector<String> copy_modes;
 		copy_modes.push_back("\n");
 		copy_modes.push_back("\n");
+		copy_modes.push_back("\n#define MODE_PANORAMA_TO_DP\n");
 
 
 		copy_to_fb.shader.initialize(copy_modes);
 		copy_to_fb.shader.initialize(copy_modes);
 
 

+ 2 - 1
servers/rendering/rasterizer_rd/rasterizer_effects_rd.h

@@ -113,6 +113,7 @@ class RasterizerEffectsRD {
 
 
 	enum CopyToFBMode {
 	enum CopyToFBMode {
 		COPY_TO_FB_COPY,
 		COPY_TO_FB_COPY,
+		COPY_TO_FB_COPY_PANORAMA_TO_DP,
 		COPY_TO_FB_MAX,
 		COPY_TO_FB_MAX,
 
 
 	};
 	};
@@ -565,7 +566,7 @@ public:
 	void copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_all_source = false, bool p_8_bit_dst = false);
 	void copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_all_source = false, bool p_8_bit_dst = false);
 	void copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false);
 	void copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false);
 	void copy_depth_to_rect_and_linearize(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, float p_z_near, float p_z_far);
 	void copy_depth_to_rect_and_linearize(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y, float p_z_near, float p_z_far);
-	void copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y = false);
+	void copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_uv_rect, RD::DrawListID p_draw_list, bool p_flip_y = false, bool p_panorama = false);
 	void gaussian_blur(RID p_source_rd_texture, RID p_texture, RID p_back_texture, const Rect2i &p_region, bool p_8bit_dst = false);
 	void gaussian_blur(RID p_source_rd_texture, RID p_texture, RID p_back_texture, const Rect2i &p_region, bool p_8bit_dst = false);
 	void gaussian_glow(RID p_source_rd_texture, RID p_texture, RID p_back_texture, const Size2i &p_size, float p_strength = 1.0, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_treshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0);
 	void gaussian_glow(RID p_source_rd_texture, RID p_texture, RID p_back_texture, const Size2i &p_size, float p_strength = 1.0, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_treshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0);
 
 

+ 25 - 7
servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp

@@ -1841,6 +1841,30 @@ void RasterizerSceneHighEndRD::_setup_lights(RID *p_light_cull_result, int p_lig
 				light_data.atlas_rect[2] = 0;
 				light_data.atlas_rect[2] = 0;
 				light_data.atlas_rect[3] = 0;
 				light_data.atlas_rect[3] = 0;
 
 
+				RID projector = storage->light_get_projector(base);
+
+				if (projector.is_valid()) {
+					Rect2 rect = storage->decal_atlas_get_texture_rect(projector);
+
+					if (type == RS::LIGHT_SPOT) {
+
+						light_data.projector_rect[0] = rect.position.x;
+						light_data.projector_rect[1] = rect.position.y + rect.size.height; //flip because shadow is flipped
+						light_data.projector_rect[2] = rect.size.width;
+						light_data.projector_rect[3] = -rect.size.height;
+					} else {
+						light_data.projector_rect[0] = rect.position.x;
+						light_data.projector_rect[1] = rect.position.y;
+						light_data.projector_rect[2] = rect.size.width;
+						light_data.projector_rect[3] = rect.size.height * 0.5; //used by dp, so needs to be half
+					}
+				} else {
+					light_data.projector_rect[0] = 0;
+					light_data.projector_rect[1] = 0;
+					light_data.projector_rect[2] = 0;
+					light_data.projector_rect[3] = 0;
+				}
+
 				if (p_using_shadows && p_shadow_atlas.is_valid() && shadow_atlas_owns_light_instance(p_shadow_atlas, li)) {
 				if (p_using_shadows && p_shadow_atlas.is_valid() && shadow_atlas_owns_light_instance(p_shadow_atlas, li)) {
 					// fill in the shadow information
 					// fill in the shadow information
 
 
@@ -1892,17 +1916,11 @@ void RasterizerSceneHighEndRD::_setup_lights(RID *p_light_cull_result, int p_lig
 
 
 					} else if (type == RS::LIGHT_SPOT) {
 					} else if (type == RS::LIGHT_SPOT) {
 
 
-						//used for clamping in this light type
-						light_data.atlas_rect[2] += light_data.atlas_rect[0];
-						light_data.atlas_rect[3] += light_data.atlas_rect[1];
-
 						Transform modelview = (p_camera_inverse_transform * light_transform).inverse();
 						Transform modelview = (p_camera_inverse_transform * light_transform).inverse();
 						CameraMatrix bias;
 						CameraMatrix bias;
 						bias.set_light_bias();
 						bias.set_light_bias();
-						CameraMatrix rectm;
-						rectm.set_light_atlas_rect(rect);
 
 
-						CameraMatrix shadow_mtx = rectm * bias * light_instance_get_shadow_camera(li, 0) * modelview;
+						CameraMatrix shadow_mtx = bias * light_instance_get_shadow_camera(li, 0) * modelview;
 						store_camera(shadow_mtx, light_data.shadow_matrix);
 						store_camera(shadow_mtx, light_data.shadow_matrix);
 
 
 						if (size > 0.0) {
 						if (size > 0.0) {

+ 1 - 0
servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h

@@ -279,6 +279,7 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 		float soft_shadow_scale;
 		float soft_shadow_scale;
 		uint32_t mask;
 		uint32_t mask;
 		uint32_t pad[2];
 		uint32_t pad[2];
+		float projector_rect[4];
 	};
 	};
 
 
 	struct DirectionalLightData {
 	struct DirectionalLightData {

+ 24 - 3
servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp

@@ -3180,7 +3180,19 @@ void RasterizerStorageRD::light_set_projector(RID p_light, RID p_texture) {
 	Light *light = light_owner.getornull(p_light);
 	Light *light = light_owner.getornull(p_light);
 	ERR_FAIL_COND(!light);
 	ERR_FAIL_COND(!light);
 
 
+	if (light->projector == p_texture) {
+		return;
+	}
+
+	if (light->type != RS::LIGHT_DIRECTIONAL && light->projector.is_valid()) {
+		texture_remove_from_decal_atlas(light->projector, light->type == RS::LIGHT_OMNI);
+	}
+
 	light->projector = p_texture;
 	light->projector = p_texture;
+
+	if (light->type != RS::LIGHT_DIRECTIONAL && light->projector.is_valid()) {
+		texture_add_to_decal_atlas(light->projector, light->type == RS::LIGHT_OMNI);
+	}
 }
 }
 
 
 void RasterizerStorageRD::light_set_negative(RID p_light, bool p_enable) {
 void RasterizerStorageRD::light_set_negative(RID p_light, bool p_enable) {
@@ -4381,22 +4393,30 @@ RS::InstanceType RasterizerStorageRD::get_base_type(RID p_rid) const {
 	return RS::INSTANCE_NONE;
 	return RS::INSTANCE_NONE;
 }
 }
 
 
-void RasterizerStorageRD::texture_add_to_decal_atlas(RID p_texture) {
+void RasterizerStorageRD::texture_add_to_decal_atlas(RID p_texture, bool p_panorama_to_dp) {
 	if (!decal_atlas.textures.has(p_texture)) {
 	if (!decal_atlas.textures.has(p_texture)) {
 		DecalAtlas::Texture t;
 		DecalAtlas::Texture t;
 		t.users = 1;
 		t.users = 1;
+		t.panorama_to_dp_users = p_panorama_to_dp ? 1 : 0;
 		decal_atlas.textures[p_texture] = t;
 		decal_atlas.textures[p_texture] = t;
 		decal_atlas.dirty = true;
 		decal_atlas.dirty = true;
 	} else {
 	} else {
 		DecalAtlas::Texture *t = decal_atlas.textures.getptr(p_texture);
 		DecalAtlas::Texture *t = decal_atlas.textures.getptr(p_texture);
 		t->users++;
 		t->users++;
+		if (p_panorama_to_dp) {
+			t->panorama_to_dp_users++;
+		}
 	}
 	}
 }
 }
 
 
-void RasterizerStorageRD::texture_remove_from_decal_atlas(RID p_texture) {
+void RasterizerStorageRD::texture_remove_from_decal_atlas(RID p_texture, bool p_panorama_to_dp) {
 	DecalAtlas::Texture *t = decal_atlas.textures.getptr(p_texture);
 	DecalAtlas::Texture *t = decal_atlas.textures.getptr(p_texture);
 	ERR_FAIL_COND(!t);
 	ERR_FAIL_COND(!t);
 	t->users--;
 	t->users--;
+	if (p_panorama_to_dp) {
+		ERR_FAIL_COND(t->panorama_to_dp_users == 0);
+		t->panorama_to_dp_users--;
+	}
 	if (t->users == 0) {
 	if (t->users == 0) {
 		decal_atlas.textures.erase(p_texture);
 		decal_atlas.textures.erase(p_texture);
 		//do not mark it dirty, there is no need to since it remains working
 		//do not mark it dirty, there is no need to since it remains working
@@ -4590,7 +4610,7 @@ void RasterizerStorageRD::_update_decal_atlas() {
 				while ((K = decal_atlas.textures.next(K))) {
 				while ((K = decal_atlas.textures.next(K))) {
 					DecalAtlas::Texture *t = decal_atlas.textures.getptr(*K);
 					DecalAtlas::Texture *t = decal_atlas.textures.getptr(*K);
 					Texture *src_tex = texture_owner.getornull(*K);
 					Texture *src_tex = texture_owner.getornull(*K);
-					effects.copy_to_atlas_fb(src_tex->rd_texture, mm.fb, t->uv_rect, draw_list);
+					effects.copy_to_atlas_fb(src_tex->rd_texture, mm.fb, t->uv_rect, draw_list, false, t->panorama_to_dp_users > 0);
 				}
 				}
 
 
 				RD::get_singleton()->draw_list_end();
 				RD::get_singleton()->draw_list_end();
@@ -4732,6 +4752,7 @@ bool RasterizerStorageRD::free(RID p_rid) {
 
 
 	} else if (light_owner.owns(p_rid)) {
 	} else if (light_owner.owns(p_rid)) {
 
 
+		light_set_projector(p_rid, RID()); //clear projector
 		// delete the texture
 		// delete the texture
 		Light *light = light_owner.getornull(p_rid);
 		Light *light = light_owner.getornull(p_rid);
 		light->instance_dependency.instance_notify_deleted(p_rid);
 		light->instance_dependency.instance_notify_deleted(p_rid);

+ 11 - 2
servers/rendering/rasterizer_rd/rasterizer_storage_rd.h

@@ -178,6 +178,7 @@ private:
 	struct DecalAtlas {
 	struct DecalAtlas {
 		struct Texture {
 		struct Texture {
 
 
+			int panorama_to_dp_users;
 			int users;
 			int users;
 			Rect2 uv_rect;
 			Rect2 uv_rect;
 		};
 		};
@@ -599,8 +600,8 @@ public:
 
 
 	virtual Size2 texture_size_with_proxy(RID p_proxy);
 	virtual Size2 texture_size_with_proxy(RID p_proxy);
 
 
-	virtual void texture_add_to_decal_atlas(RID p_texture);
-	virtual void texture_remove_from_decal_atlas(RID p_texture);
+	virtual void texture_add_to_decal_atlas(RID p_texture, bool p_panorama_to_dp = false);
+	virtual void texture_remove_from_decal_atlas(RID p_texture, bool p_panorama_to_dp = false);
 
 
 	RID decal_atlas_get_texture() const;
 	RID decal_atlas_get_texture() const;
 	RID decal_atlas_get_texture_srgb() const;
 	RID decal_atlas_get_texture_srgb() const;
@@ -964,6 +965,14 @@ public:
 		return light->param[p_param];
 		return light->param[p_param];
 	}
 	}
 
 
+	_FORCE_INLINE_ RID light_get_projector(RID p_light) {
+
+		const Light *light = light_owner.getornull(p_light);
+		ERR_FAIL_COND_V(!light, RID());
+
+		return light->projector;
+	}
+
 	_FORCE_INLINE_ Color light_get_color(RID p_light) {
 	_FORCE_INLINE_ Color light_get_color(RID p_light) {
 
 
 		const Light *light = light_owner.getornull(p_light);
 		const Light *light = light_owner.getornull(p_light);

+ 28 - 0
servers/rendering/rasterizer_rd/shaders/copy_to_fb.glsl

@@ -65,6 +65,34 @@ layout(location = 0) out vec4 frag_color;
 void main() {
 void main() {
 
 
 	vec2 uv = uv_interp;
 	vec2 uv = uv_interp;
+
+#ifdef MODE_PANORAMA_TO_DP
+
+	//obtain normal from dual paraboloid uv
+#define M_PI 3.14159265359
+
+	float side;
+	uv.y = modf(uv.y * 2.0, side);
+	side = side * 2.0 - 1.0;
+	vec3 normal = vec3(uv * 2.0 - 1.0, 0.0);
+	normal.z = 0.5 - 0.5 * ((normal.x * normal.x) + (normal.y * normal.y));
+	normal *= -side;
+	normal = normalize(normal);
+
+	//now convert normal to panorama uv
+
+	vec2 st = vec2(atan(normal.x, normal.z), acos(normal.y));
+
+	if (st.x < 0.0)
+		st.x += M_PI * 2.0;
+
+	uv = st / vec2(M_PI * 2.0, M_PI);
+
+	if (side < 0.0) {
+		//uv.y = 1.0 - uv.y;
+		uv = 1.0 - uv;
+	}
+#endif
 	vec4 color = textureLod(source_color, uv, 0.0);
 	vec4 color = textureLod(source_color, uv, 0.0);
 	if (params.force_luminance) {
 	if (params.force_luminance) {
 		color.rgb = vec3(max(max(color.r, color.g), color.b));
 		color.rgb = vec3(max(max(color.r, color.g), color.b));

+ 108 - 17
servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl

@@ -784,7 +784,7 @@ float sample_directional_soft_shadow(texture2D shadow, vec3 pssm_coord, vec2 tex
 
 
 #endif //USE_NO_SHADOWS
 #endif //USE_NO_SHADOWS
 
 
-void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 albedo, float roughness, float metallic, float specular, float p_blob_intensity,
+void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 vertex_ddx, vec3 vertex_ddy, vec3 albedo, float roughness, float metallic, float specular, float p_blob_intensity,
 #ifdef LIGHT_BACKLIGHT_USED
 #ifdef LIGHT_BACKLIGHT_USED
 		vec3 backlight,
 		vec3 backlight,
 #endif
 #endif
@@ -976,8 +976,8 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 a
 			//redo shadowmapping, but shrink the model a bit to avoid arctifacts
 			//redo shadowmapping, but shrink the model a bit to avoid arctifacts
 			splane = (lights.data[idx].shadow_matrix * vec4(vertex - normalize(normal_interp) * lights.data[idx].transmittance_bias, 1.0));
 			splane = (lights.data[idx].shadow_matrix * vec4(vertex - normalize(normal_interp) * lights.data[idx].transmittance_bias, 1.0));
 
 
-			shadow_len = length(splane);
-			splane = normalize(splane);
+			shadow_len = length(splane.xyz);
+			splane = normalize(splane.xyz);
 
 
 			if (splane.z >= 0.0) {
 			if (splane.z >= 0.0) {
 
 
@@ -999,7 +999,70 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 a
 		}
 		}
 #endif
 #endif
 
 
-		shadow_attenuation = mix(shadow_color_enabled.rgb, vec3(1.0), shadow);
+		vec3 no_shadow = vec3(1.0);
+
+		if (lights.data[idx].projector_rect != vec4(0.0)) {
+
+			vec3 local_v = (lights.data[idx].shadow_matrix * vec4(vertex, 1.0)).xyz;
+			local_v = normalize(local_v);
+
+			vec4 atlas_rect = lights.data[idx].projector_rect;
+
+			if (local_v.z >= 0.0) {
+
+				local_v.z += 1.0;
+				atlas_rect.y += atlas_rect.w;
+
+			} else {
+
+				local_v.z = 1.0 - local_v.z;
+			}
+
+			local_v.xy /= local_v.z;
+			local_v.xy = local_v.xy * 0.5 + 0.5;
+			vec2 proj_uv = local_v.xy * atlas_rect.zw;
+
+			vec2 proj_uv_ddx;
+			vec2 proj_uv_ddy;
+			{
+				vec3 local_v_ddx = (lights.data[idx].shadow_matrix * vec4(vertex + vertex_ddx, 1.0)).xyz;
+				local_v_ddx = normalize(local_v_ddx);
+
+				if (local_v_ddx.z >= 0.0) {
+
+					local_v_ddx.z += 1.0;
+				} else {
+
+					local_v_ddx.z = 1.0 - local_v_ddx.z;
+				}
+
+				local_v_ddx.xy /= local_v_ddx.z;
+				local_v_ddx.xy = local_v_ddx.xy * 0.5 + 0.5;
+
+				proj_uv_ddx = local_v_ddx.xy * atlas_rect.zw - proj_uv;
+
+				vec3 local_v_ddy = (lights.data[idx].shadow_matrix * vec4(vertex + vertex_ddy, 1.0)).xyz;
+				local_v_ddy = normalize(local_v_ddy);
+
+				if (local_v_ddy.z >= 0.0) {
+
+					local_v_ddy.z += 1.0;
+				} else {
+
+					local_v_ddy.z = 1.0 - local_v_ddy.z;
+				}
+
+				local_v_ddy.xy /= local_v_ddy.z;
+				local_v_ddy.xy = local_v_ddy.xy * 0.5 + 0.5;
+
+				proj_uv_ddy = local_v_ddy.xy * atlas_rect.zw - proj_uv;
+			}
+
+			vec4 proj = textureGrad(sampler2D(decal_atlas_srgb, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), proj_uv + atlas_rect.xy, proj_uv_ddx, proj_uv_ddy);
+			no_shadow = mix(no_shadow, proj.rgb, proj.a);
+		}
+
+		shadow_attenuation = mix(shadow_color_enabled.rgb, no_shadow, shadow);
 	}
 	}
 #endif //USE_NO_SHADOWS
 #endif //USE_NO_SHADOWS
 
 
@@ -1030,7 +1093,7 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 a
 			specular_light);
 			specular_light);
 }
 }
 
 
-void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 albedo, float roughness, float metallic, float specular, float p_blob_intensity,
+void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 vertex_ddx, vec3 vertex_ddy, vec3 albedo, float roughness, float metallic, float specular, float p_blob_intensity,
 #ifdef LIGHT_BACKLIGHT_USED
 #ifdef LIGHT_BACKLIGHT_USED
 		vec3 backlight,
 		vec3 backlight,
 #endif
 #endif
@@ -1114,6 +1177,8 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 a
 
 
 			//find blocker
 			//find blocker
 
 
+			vec2 shadow_uv = splane.xy * lights.data[idx].atlas_rect.zw + lights.data[idx].atlas_rect.xy;
+
 			float blocker_count = 0.0;
 			float blocker_count = 0.0;
 			float blocker_average = 0.0;
 			float blocker_average = 0.0;
 
 
@@ -1126,10 +1191,11 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 a
 			}
 			}
 
 
 			float uv_size = lights.data[idx].soft_shadow_size * z_norm * lights.data[idx].soft_shadow_scale;
 			float uv_size = lights.data[idx].soft_shadow_size * z_norm * lights.data[idx].soft_shadow_scale;
+			vec2 clamp_max = lights.data[idx].atlas_rect.xy + lights.data[idx].atlas_rect.zw;
 			for (uint i = 0; i < scene_data.penumbra_shadow_samples; i++) {
 			for (uint i = 0; i < scene_data.penumbra_shadow_samples; i++) {
 
 
-				vec2 suv = splane.xy + (disk_rotation * scene_data.penumbra_shadow_kernel[i].xy) * uv_size;
-				suv = clamp(suv, lights.data[idx].atlas_rect.xy, lights.data[idx].atlas_rect.zw);
+				vec2 suv = shadow_uv + (disk_rotation * scene_data.penumbra_shadow_kernel[i].xy) * uv_size;
+				suv = clamp(suv, lights.data[idx].atlas_rect.xy, clamp_max);
 				float d = textureLod(sampler2D(shadow_atlas, material_samplers[SAMPLER_LINEAR_CLAMP]), suv, 0.0).r;
 				float d = textureLod(sampler2D(shadow_atlas, material_samplers[SAMPLER_LINEAR_CLAMP]), suv, 0.0).r;
 				if (d < z_norm) {
 				if (d < z_norm) {
 					blocker_average += d;
 					blocker_average += d;
@@ -1146,8 +1212,8 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 a
 
 
 				shadow = 0.0;
 				shadow = 0.0;
 				for (uint i = 0; i < scene_data.penumbra_shadow_samples; i++) {
 				for (uint i = 0; i < scene_data.penumbra_shadow_samples; i++) {
-					vec2 suv = splane.xy + (disk_rotation * scene_data.penumbra_shadow_kernel[i].xy) * uv_size;
-					suv = clamp(suv, lights.data[idx].atlas_rect.xy, lights.data[idx].atlas_rect.zw);
+					vec2 suv = shadow_uv + (disk_rotation * scene_data.penumbra_shadow_kernel[i].xy) * uv_size;
+					suv = clamp(suv, lights.data[idx].atlas_rect.xy, clamp_max);
 					shadow += textureProj(sampler2DShadow(shadow_atlas, shadow_sampler), vec4(suv, z_norm, 1.0));
 					shadow += textureProj(sampler2DShadow(shadow_atlas, shadow_sampler), vec4(suv, z_norm, 1.0));
 				}
 				}
 
 
@@ -1160,17 +1226,41 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 a
 
 
 		} else {
 		} else {
 			//hard shadow
 			//hard shadow
-			splane.z = z_norm;
-			shadow = sample_pcf_shadow(shadow_atlas, lights.data[idx].soft_shadow_scale * scene_data.shadow_atlas_pixel_size, splane);
+			vec4 shadow_uv = vec4(splane.xy * lights.data[idx].atlas_rect.zw + lights.data[idx].atlas_rect.xy, z_norm, 1.0);
+
+			shadow = sample_pcf_shadow(shadow_atlas, lights.data[idx].soft_shadow_scale * scene_data.shadow_atlas_pixel_size, shadow_uv);
 		}
 		}
 
 
-		shadow_attenuation = mix(shadow_color_enabled.rgb, vec3(1.0), shadow);
+		vec3 no_shadow = vec3(1.0);
+
+		if (lights.data[idx].projector_rect != vec4(0.0)) {
+
+			splane = (lights.data[idx].shadow_matrix * vec4(vertex, 1.0));
+			splane /= splane.w;
+
+			vec2 proj_uv = splane.xy * lights.data[idx].projector_rect.zw;
+
+			//ensure we have proper mipmaps
+			vec4 splane_ddx = (lights.data[idx].shadow_matrix * vec4(vertex + vertex_ddx, 1.0));
+			splane_ddx /= splane_ddx.w;
+			vec2 proj_uv_ddx = splane_ddx.xy * lights.data[idx].projector_rect.zw - proj_uv;
+
+			vec4 splane_ddy = (lights.data[idx].shadow_matrix * vec4(vertex + vertex_ddy, 1.0));
+			splane_ddy /= splane_ddy.w;
+			vec2 proj_uv_ddy = splane_ddy.xy * lights.data[idx].projector_rect.zw - proj_uv;
+
+			vec4 proj = textureGrad(sampler2D(decal_atlas_srgb, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), proj_uv + lights.data[idx].projector_rect.xy, proj_uv_ddx, proj_uv_ddy);
+			no_shadow = mix(no_shadow, proj.rgb, proj.a);
+		}
+
+		shadow_attenuation = mix(shadow_color_enabled.rgb, no_shadow, shadow);
 
 
 #ifdef LIGHT_TRANSMITTANCE_USED
 #ifdef LIGHT_TRANSMITTANCE_USED
 		{
 		{
 
 
-			vec4 splane = (lights.data[idx].shadow_matrix * vec4(vertex - normalize(normal_interp) * lights.data[idx].transmittance_bias, 1.0));
+			splane = (lights.data[idx].shadow_matrix * vec4(vertex - normalize(normal_interp) * lights.data[idx].transmittance_bias, 1.0));
 			splane /= splane.w;
 			splane /= splane.w;
+			splane.xy = splane.xy * lights.data[idx].atlas_rect.zw + lights.data[idx].atlas_rect.xy;
 
 
 			float shadow_z = textureLod(sampler2D(shadow_atlas, material_samplers[SAMPLER_LINEAR_CLAMP]), splane.xy, 0.0).r;
 			float shadow_z = textureLod(sampler2D(shadow_atlas, material_samplers[SAMPLER_LINEAR_CLAMP]), splane.xy, 0.0).r;
 			//reconstruct depth
 			//reconstruct depth
@@ -1691,6 +1781,9 @@ FRAGMENT_SHADER_CODE
 #ifndef MODE_RENDER_DEPTH
 #ifndef MODE_RENDER_DEPTH
 
 
 	uvec4 cluster_cell = texture(usampler3D(cluster_texture, material_samplers[SAMPLER_NEAREST_CLAMP]), vec3(screen_uv, (abs(vertex.z) - scene_data.z_near) / (scene_data.z_far - scene_data.z_near)));
 	uvec4 cluster_cell = texture(usampler3D(cluster_texture, material_samplers[SAMPLER_NEAREST_CLAMP]), vec3(screen_uv, (abs(vertex.z) - scene_data.z_near) / (scene_data.z_far - scene_data.z_near)));
+	//used for interpolating anything cluster related
+	vec3 vertex_ddx = dFdx(vertex);
+	vec3 vertex_ddy = dFdy(vertex);
 
 
 	{ // process decals
 	{ // process decals
 
 
@@ -1698,8 +1791,6 @@ FRAGMENT_SHADER_CODE
 		uint decal_pointer = cluster_cell.w & CLUSTER_POINTER_MASK;
 		uint decal_pointer = cluster_cell.w & CLUSTER_POINTER_MASK;
 
 
 		//do outside for performance and avoiding arctifacts
 		//do outside for performance and avoiding arctifacts
-		vec3 vertex_ddx = dFdx(vertex);
-		vec3 vertex_ddy = dFdy(vertex);
 
 
 		for (uint i = 0; i < decal_count; i++) {
 		for (uint i = 0; i < decal_count; i++) {
 
 
@@ -2195,7 +2286,7 @@ FRAGMENT_SHADER_CODE
 				continue; //not masked
 				continue; //not masked
 			}
 			}
 
 
-			light_process_omni(light_index, vertex, view, normal, albedo, roughness, metallic, specular, specular_blob_intensity,
+			light_process_omni(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, albedo, roughness, metallic, specular, specular_blob_intensity,
 #ifdef LIGHT_BACKLIGHT_USED
 #ifdef LIGHT_BACKLIGHT_USED
 					backlight,
 					backlight,
 #endif
 #endif
@@ -2234,7 +2325,7 @@ FRAGMENT_SHADER_CODE
 				continue; //not masked
 				continue; //not masked
 			}
 			}
 
 
-			light_process_spot(light_index, vertex, view, normal, albedo, roughness, metallic, specular, specular_blob_intensity,
+			light_process_spot(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, albedo, roughness, metallic, specular, specular_blob_intensity,
 #ifdef LIGHT_BACKLIGHT_USED
 #ifdef LIGHT_BACKLIGHT_USED
 					backlight,
 					backlight,
 #endif
 #endif

+ 2 - 1
servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl

@@ -153,7 +153,7 @@ struct LightData { //this structure needs to be as packed as possible
 	uint color_specular; //rgb color, a specular (8 bit unorm)
 	uint color_specular; //rgb color, a specular (8 bit unorm)
 	uint cone_attenuation_angle; // attenuation and angle, (16bit float)
 	uint cone_attenuation_angle; // attenuation and angle, (16bit float)
 	uint shadow_color_enabled; //shadow rgb color, a>0.5 enabled (8bit unorm)
 	uint shadow_color_enabled; //shadow rgb color, a>0.5 enabled (8bit unorm)
-	vec4 atlas_rect; // used for spot
+	vec4 atlas_rect; // rect in the shadow atlas
 	mat4 shadow_matrix;
 	mat4 shadow_matrix;
 	float shadow_bias;
 	float shadow_bias;
 	float shadow_normal_bias;
 	float shadow_normal_bias;
@@ -162,6 +162,7 @@ struct LightData { //this structure needs to be as packed as possible
 	float soft_shadow_scale; // scales the shadow kernel for blurrier shadows
 	float soft_shadow_scale; // scales the shadow kernel for blurrier shadows
 	uint mask;
 	uint mask;
 	uint pad[2];
 	uint pad[2];
+	vec4 projector_rect; //projector rect in srgb decal atlas
 };
 };
 
 
 layout(set = 0, binding = 5, std430) restrict readonly buffer Lights {
 layout(set = 0, binding = 5, std430) restrict readonly buffer Lights {