Browse Source

Implement DirectionalLight2D

Also separated Light2D in PointLight2D and DirectionalLight2D.
Used PointLight2D because its more of a point, and it does not work
the same as OmniLight (as shape depends on texture).
Added a few utility methods to Rect2D I needed.
reduz 4 years ago
parent
commit
f123981a96

+ 62 - 0
core/math/rect2.h

@@ -244,6 +244,68 @@ struct Rect2 {
 		return Rect2(Point2(position.x + MIN(size.x, 0), position.y + MIN(size.y, 0)), size.abs());
 		return Rect2(Point2(position.x + MIN(size.x, 0), position.y + MIN(size.y, 0)), size.abs());
 	}
 	}
 
 
+	Vector2 get_support(const Vector2 &p_normal) const {
+		Vector2 half_extents = size * 0.5;
+		Vector2 ofs = position + half_extents;
+		return Vector2(
+					   (p_normal.x > 0) ? -half_extents.x : half_extents.x,
+					   (p_normal.y > 0) ? -half_extents.y : half_extents.y) +
+			   ofs;
+	}
+
+	_FORCE_INLINE_ bool intersects_filled_polygon(const Vector2 *p_points, int p_point_count) const {
+		Vector2 center = position + size * 0.5;
+		int side_plus = 0;
+		int side_minus = 0;
+		Vector2 end = position + size;
+
+		int i_f = p_point_count - 1;
+		for (int i = 0; i < p_point_count; i++) {
+			const Vector2 &a = p_points[i_f];
+			const Vector2 &b = p_points[i];
+			i_f = i;
+
+			Vector2 r = (b - a);
+			float l = r.length();
+			if (l == 0.0) {
+				continue;
+			}
+
+			//check inside
+			Vector2 tg = r.tangent();
+			float s = tg.dot(center) - tg.dot(a);
+			if (s < 0.0) {
+				side_plus++;
+			} else {
+				side_minus++;
+			}
+
+			//check ray box
+			r /= l;
+			Vector2 ir(1.0 / r.x, 1.0 / r.y);
+
+			// lb is the corner of AABB with minimal coordinates - left bottom, rt is maximal corner
+			// r.org is origin of ray
+			Vector2 t13 = (position - a) * ir;
+			Vector2 t24 = (end - a) * ir;
+
+			float tmin = MAX(MIN(t13.x, t24.x), MIN(t13.y, t24.y));
+			float tmax = MIN(MAX(t13.x, t24.x), MAX(t13.y, t24.y));
+
+			// if tmax < 0, ray (line) is intersecting AABB, but the whole AABB is behind us
+			if (tmax < 0 || tmin > tmax || tmin >= l) {
+				continue;
+			}
+
+			return true;
+		}
+
+		if (side_plus * side_minus == 0) {
+			return true; //all inside
+		} else {
+			return false;
+		}
+	}
 	operator String() const { return String(position) + ", " + String(size); }
 	operator String() const { return String(position) + ", " + String(size); }
 
 
 	Rect2() {}
 	Rect2() {}

+ 171 - 138
scene/2d/light_2d.cpp

@@ -33,54 +33,6 @@
 #include "core/engine.h"
 #include "core/engine.h"
 #include "servers/rendering_server.h"
 #include "servers/rendering_server.h"
 
 
-#ifdef TOOLS_ENABLED
-Dictionary Light2D::_edit_get_state() const {
-	Dictionary state = Node2D::_edit_get_state();
-	state["offset"] = get_texture_offset();
-	return state;
-}
-
-void Light2D::_edit_set_state(const Dictionary &p_state) {
-	Node2D::_edit_set_state(p_state);
-	set_texture_offset(p_state["offset"]);
-}
-
-void Light2D::_edit_set_pivot(const Point2 &p_pivot) {
-	set_position(get_transform().xform(p_pivot));
-	set_texture_offset(get_texture_offset() - p_pivot);
-}
-
-Point2 Light2D::_edit_get_pivot() const {
-	return Vector2();
-}
-
-bool Light2D::_edit_use_pivot() const {
-	return true;
-}
-
-Rect2 Light2D::_edit_get_rect() const {
-	if (texture.is_null()) {
-		return Rect2();
-	}
-
-	Size2 s = texture->get_size() * _scale;
-	return Rect2(texture_offset - s / 2.0, s);
-}
-
-bool Light2D::_edit_use_rect() const {
-	return !texture.is_null();
-}
-#endif
-
-Rect2 Light2D::get_anchorable_rect() const {
-	if (texture.is_null()) {
-		return Rect2();
-	}
-
-	Size2 s = texture->get_size() * _scale;
-	return Rect2(texture_offset - s / 2.0, s);
-}
-
 void Light2D::_update_light_visibility() {
 void Light2D::_update_light_visibility() {
 	if (!is_inside_tree()) {
 	if (!is_inside_tree()) {
 		return;
 		return;
@@ -123,32 +75,6 @@ bool Light2D::is_editor_only() const {
 	return editor_only;
 	return editor_only;
 }
 }
 
 
-void Light2D::set_texture(const Ref<Texture2D> &p_texture) {
-	texture = p_texture;
-	if (texture.is_valid()) {
-		RS::get_singleton()->canvas_light_set_texture(canvas_light, texture->get_rid());
-	} else {
-		RS::get_singleton()->canvas_light_set_texture(canvas_light, RID());
-	}
-
-	update_configuration_warning();
-}
-
-Ref<Texture2D> Light2D::get_texture() const {
-	return texture;
-}
-
-void Light2D::set_texture_offset(const Vector2 &p_offset) {
-	texture_offset = p_offset;
-	RS::get_singleton()->canvas_light_set_texture_offset(canvas_light, texture_offset);
-	item_rect_changed();
-	_change_notify("offset");
-}
-
-Vector2 Light2D::get_texture_offset() const {
-	return texture_offset;
-}
-
 void Light2D::set_color(const Color &p_color) {
 void Light2D::set_color(const Color &p_color) {
 	color = p_color;
 	color = p_color;
 	RS::get_singleton()->canvas_light_set_color(canvas_light, color);
 	RS::get_singleton()->canvas_light_set_color(canvas_light, color);
@@ -176,20 +102,6 @@ float Light2D::get_energy() const {
 	return energy;
 	return energy;
 }
 }
 
 
-void Light2D::set_texture_scale(float p_scale) {
-	_scale = p_scale;
-	// Avoid having 0 scale values, can lead to errors in physics and rendering.
-	if (_scale == 0) {
-		_scale = CMP_EPSILON;
-	}
-	RS::get_singleton()->canvas_light_set_scale(canvas_light, _scale);
-	item_rect_changed();
-}
-
-float Light2D::get_texture_scale() const {
-	return _scale;
-}
-
 void Light2D::set_z_range_min(int p_min_z) {
 void Light2D::set_z_range_min(int p_min_z) {
 	z_min = p_min_z;
 	z_min = p_min_z;
 	RS::get_singleton()->canvas_light_set_z_range(canvas_light, z_min, z_max);
 	RS::get_singleton()->canvas_light_set_z_range(canvas_light, z_min, z_max);
@@ -244,15 +156,6 @@ int Light2D::get_item_shadow_cull_mask() const {
 	return item_shadow_mask;
 	return item_shadow_mask;
 }
 }
 
 
-void Light2D::set_mode(Mode p_mode) {
-	mode = p_mode;
-	RS::get_singleton()->canvas_light_set_mode(canvas_light, RS::CanvasLightMode(p_mode));
-}
-
-Light2D::Mode Light2D::get_mode() const {
-	return mode;
-}
-
 void Light2D::set_shadow_enabled(bool p_enabled) {
 void Light2D::set_shadow_enabled(bool p_enabled) {
 	shadow = p_enabled;
 	shadow = p_enabled;
 	RS::get_singleton()->canvas_light_set_shadow_enabled(canvas_light, shadow);
 	RS::get_singleton()->canvas_light_set_shadow_enabled(canvas_light, shadow);
@@ -281,6 +184,15 @@ Color Light2D::get_shadow_color() const {
 	return shadow_color;
 	return shadow_color;
 }
 }
 
 
+void Light2D::set_blend_mode(BlendMode p_mode) {
+	blend_mode = p_mode;
+	RS::get_singleton()->canvas_light_set_blend_mode(_get_light(), RS::CanvasLightBlendMode(p_mode));
+}
+
+Light2D::BlendMode Light2D::get_blend_mode() const {
+	return blend_mode;
+}
+
 void Light2D::_notification(int p_what) {
 void Light2D::_notification(int p_what) {
 	if (p_what == NOTIFICATION_ENTER_TREE) {
 	if (p_what == NOTIFICATION_ENTER_TREE) {
 		RS::get_singleton()->canvas_light_attach_to_canvas(canvas_light, get_canvas());
 		RS::get_singleton()->canvas_light_attach_to_canvas(canvas_light, get_canvas());
@@ -300,19 +212,6 @@ void Light2D::_notification(int p_what) {
 	}
 	}
 }
 }
 
 
-String Light2D::get_configuration_warning() const {
-	String warning = Node2D::get_configuration_warning();
-
-	if (!texture.is_valid()) {
-		if (!warning.empty()) {
-			warning += "\n\n";
-		}
-		warning += TTR("A texture with the shape of the light must be supplied to the \"Texture\" property.");
-	}
-
-	return warning;
-}
-
 void Light2D::set_shadow_smooth(float p_amount) {
 void Light2D::set_shadow_smooth(float p_amount) {
 	shadow_smooth = p_amount;
 	shadow_smooth = p_amount;
 	RS::get_singleton()->canvas_light_set_shadow_smooth(canvas_light, shadow_smooth);
 	RS::get_singleton()->canvas_light_set_shadow_smooth(canvas_light, shadow_smooth);
@@ -329,24 +228,12 @@ void Light2D::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_editor_only", "editor_only"), &Light2D::set_editor_only);
 	ClassDB::bind_method(D_METHOD("set_editor_only", "editor_only"), &Light2D::set_editor_only);
 	ClassDB::bind_method(D_METHOD("is_editor_only"), &Light2D::is_editor_only);
 	ClassDB::bind_method(D_METHOD("is_editor_only"), &Light2D::is_editor_only);
 
 
-	ClassDB::bind_method(D_METHOD("set_texture", "texture"), &Light2D::set_texture);
-	ClassDB::bind_method(D_METHOD("get_texture"), &Light2D::get_texture);
-
-	ClassDB::bind_method(D_METHOD("set_texture_offset", "texture_offset"), &Light2D::set_texture_offset);
-	ClassDB::bind_method(D_METHOD("get_texture_offset"), &Light2D::get_texture_offset);
-
 	ClassDB::bind_method(D_METHOD("set_color", "color"), &Light2D::set_color);
 	ClassDB::bind_method(D_METHOD("set_color", "color"), &Light2D::set_color);
 	ClassDB::bind_method(D_METHOD("get_color"), &Light2D::get_color);
 	ClassDB::bind_method(D_METHOD("get_color"), &Light2D::get_color);
 
 
-	ClassDB::bind_method(D_METHOD("set_height", "height"), &Light2D::set_height);
-	ClassDB::bind_method(D_METHOD("get_height"), &Light2D::get_height);
-
 	ClassDB::bind_method(D_METHOD("set_energy", "energy"), &Light2D::set_energy);
 	ClassDB::bind_method(D_METHOD("set_energy", "energy"), &Light2D::set_energy);
 	ClassDB::bind_method(D_METHOD("get_energy"), &Light2D::get_energy);
 	ClassDB::bind_method(D_METHOD("get_energy"), &Light2D::get_energy);
 
 
-	ClassDB::bind_method(D_METHOD("set_texture_scale", "texture_scale"), &Light2D::set_texture_scale);
-	ClassDB::bind_method(D_METHOD("get_texture_scale"), &Light2D::get_texture_scale);
-
 	ClassDB::bind_method(D_METHOD("set_z_range_min", "z"), &Light2D::set_z_range_min);
 	ClassDB::bind_method(D_METHOD("set_z_range_min", "z"), &Light2D::set_z_range_min);
 	ClassDB::bind_method(D_METHOD("get_z_range_min"), &Light2D::get_z_range_min);
 	ClassDB::bind_method(D_METHOD("get_z_range_min"), &Light2D::get_z_range_min);
 
 
@@ -365,9 +252,6 @@ void Light2D::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_item_shadow_cull_mask", "item_shadow_cull_mask"), &Light2D::set_item_shadow_cull_mask);
 	ClassDB::bind_method(D_METHOD("set_item_shadow_cull_mask", "item_shadow_cull_mask"), &Light2D::set_item_shadow_cull_mask);
 	ClassDB::bind_method(D_METHOD("get_item_shadow_cull_mask"), &Light2D::get_item_shadow_cull_mask);
 	ClassDB::bind_method(D_METHOD("get_item_shadow_cull_mask"), &Light2D::get_item_shadow_cull_mask);
 
 
-	ClassDB::bind_method(D_METHOD("set_mode", "mode"), &Light2D::set_mode);
-	ClassDB::bind_method(D_METHOD("get_mode"), &Light2D::get_mode);
-
 	ClassDB::bind_method(D_METHOD("set_shadow_enabled", "enabled"), &Light2D::set_shadow_enabled);
 	ClassDB::bind_method(D_METHOD("set_shadow_enabled", "enabled"), &Light2D::set_shadow_enabled);
 	ClassDB::bind_method(D_METHOD("is_shadow_enabled"), &Light2D::is_shadow_enabled);
 	ClassDB::bind_method(D_METHOD("is_shadow_enabled"), &Light2D::is_shadow_enabled);
 
 
@@ -380,16 +264,18 @@ void Light2D::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_shadow_color", "shadow_color"), &Light2D::set_shadow_color);
 	ClassDB::bind_method(D_METHOD("set_shadow_color", "shadow_color"), &Light2D::set_shadow_color);
 	ClassDB::bind_method(D_METHOD("get_shadow_color"), &Light2D::get_shadow_color);
 	ClassDB::bind_method(D_METHOD("get_shadow_color"), &Light2D::get_shadow_color);
 
 
+	ClassDB::bind_method(D_METHOD("set_blend_mode", "mode"), &Light2D::set_blend_mode);
+	ClassDB::bind_method(D_METHOD("get_blend_mode"), &Light2D::get_blend_mode);
+
+	ClassDB::bind_method(D_METHOD("set_height", "height"), &Light2D::set_height);
+	ClassDB::bind_method(D_METHOD("get_height"), &Light2D::get_height);
+
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_only"), "set_editor_only", "is_editor_only");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_only"), "set_editor_only", "is_editor_only");
-	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
-	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_texture_offset", "get_texture_offset");
-	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "texture_scale", PROPERTY_HINT_RANGE, "0.01,50,0.01"), "set_texture_scale", "get_texture_scale");
 	ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
 	ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_energy", "get_energy");
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_energy", "get_energy");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Add,Sub,Mix,Mask"), "set_mode", "get_mode");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Add,Sub,Mix"), "set_blend_mode", "get_blend_mode");
 	ADD_GROUP("Range", "range_");
 	ADD_GROUP("Range", "range_");
-	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "range_height", PROPERTY_HINT_RANGE, "-2048,2048,0.1,or_lesser,or_greater"), "set_height", "get_height");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "range_z_min", PROPERTY_HINT_RANGE, itos(RS::CANVAS_ITEM_Z_MIN) + "," + itos(RS::CANVAS_ITEM_Z_MAX) + ",1"), "set_z_range_min", "get_z_range_min");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "range_z_min", PROPERTY_HINT_RANGE, itos(RS::CANVAS_ITEM_Z_MIN) + "," + itos(RS::CANVAS_ITEM_Z_MAX) + ",1"), "set_z_range_min", "get_z_range_min");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "range_z_max", PROPERTY_HINT_RANGE, itos(RS::CANVAS_ITEM_Z_MIN) + "," + itos(RS::CANVAS_ITEM_Z_MAX) + ",1"), "set_z_range_max", "get_z_range_max");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "range_z_max", PROPERTY_HINT_RANGE, itos(RS::CANVAS_ITEM_Z_MIN) + "," + itos(RS::CANVAS_ITEM_Z_MAX) + ",1"), "set_z_range_max", "get_z_range_max");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "range_layer_min", PROPERTY_HINT_RANGE, "-512,512,1"), "set_layer_range_min", "get_layer_range_min");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "range_layer_min", PROPERTY_HINT_RANGE, "-512,512,1"), "set_layer_range_min", "get_layer_range_min");
@@ -403,14 +289,13 @@ void Light2D::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "shadow_filter_smooth", PROPERTY_HINT_RANGE, "0,64,0.1"), "set_shadow_smooth", "get_shadow_smooth");
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "shadow_filter_smooth", PROPERTY_HINT_RANGE, "0,64,0.1"), "set_shadow_smooth", "get_shadow_smooth");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_item_cull_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_item_shadow_cull_mask", "get_item_shadow_cull_mask");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_item_cull_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_item_shadow_cull_mask", "get_item_shadow_cull_mask");
 
 
-	BIND_ENUM_CONSTANT(MODE_ADD);
-	BIND_ENUM_CONSTANT(MODE_SUB);
-	BIND_ENUM_CONSTANT(MODE_MIX);
-	BIND_ENUM_CONSTANT(MODE_MASK);
-
 	BIND_ENUM_CONSTANT(SHADOW_FILTER_NONE);
 	BIND_ENUM_CONSTANT(SHADOW_FILTER_NONE);
 	BIND_ENUM_CONSTANT(SHADOW_FILTER_PCF5);
 	BIND_ENUM_CONSTANT(SHADOW_FILTER_PCF5);
 	BIND_ENUM_CONSTANT(SHADOW_FILTER_PCF13);
 	BIND_ENUM_CONSTANT(SHADOW_FILTER_PCF13);
+
+	BIND_ENUM_CONSTANT(BLEND_MODE_ADD);
+	BIND_ENUM_CONSTANT(BLEND_MODE_SUB);
+	BIND_ENUM_CONSTANT(BLEND_MODE_MIX);
 }
 }
 
 
 Light2D::Light2D() {
 Light2D::Light2D() {
@@ -420,22 +305,170 @@ Light2D::Light2D() {
 	shadow = false;
 	shadow = false;
 	color = Color(1, 1, 1);
 	color = Color(1, 1, 1);
 	height = 0;
 	height = 0;
-	_scale = 1.0;
 	z_min = -1024;
 	z_min = -1024;
 	z_max = 1024;
 	z_max = 1024;
 	layer_min = 0;
 	layer_min = 0;
 	layer_max = 0;
 	layer_max = 0;
 	item_mask = 1;
 	item_mask = 1;
 	item_shadow_mask = 1;
 	item_shadow_mask = 1;
-	mode = MODE_ADD;
 	energy = 1.0;
 	energy = 1.0;
 	shadow_color = Color(0, 0, 0, 0);
 	shadow_color = Color(0, 0, 0, 0);
 	shadow_filter = SHADOW_FILTER_NONE;
 	shadow_filter = SHADOW_FILTER_NONE;
 	shadow_smooth = 0;
 	shadow_smooth = 0;
-
+	blend_mode = BLEND_MODE_ADD;
 	set_notify_transform(true);
 	set_notify_transform(true);
 }
 }
 
 
 Light2D::~Light2D() {
 Light2D::~Light2D() {
 	RenderingServer::get_singleton()->free(canvas_light);
 	RenderingServer::get_singleton()->free(canvas_light);
 }
 }
+
+//////////////////////////////
+
+#ifdef TOOLS_ENABLED
+
+Dictionary PointLight2D::_edit_get_state() const {
+	Dictionary state = Node2D::_edit_get_state();
+	state["offset"] = get_texture_offset();
+	return state;
+}
+
+void PointLight2D::_edit_set_state(const Dictionary &p_state) {
+	Node2D::_edit_set_state(p_state);
+	set_texture_offset(p_state["offset"]);
+}
+
+void PointLight2D::_edit_set_pivot(const Point2 &p_pivot) {
+	set_position(get_transform().xform(p_pivot));
+	set_texture_offset(get_texture_offset() - p_pivot);
+}
+
+Point2 PointLight2D::_edit_get_pivot() const {
+	return Vector2();
+}
+
+bool PointLight2D::_edit_use_pivot() const {
+	return true;
+}
+
+Rect2 PointLight2D::_edit_get_rect() const {
+	if (texture.is_null()) {
+		return Rect2();
+	}
+
+	Size2 s = texture->get_size() * _scale;
+	return Rect2(texture_offset - s / 2.0, s);
+}
+
+bool PointLight2D::_edit_use_rect() const {
+	return !texture.is_null();
+}
+#endif
+
+Rect2 PointLight2D::get_anchorable_rect() const {
+	if (texture.is_null()) {
+		return Rect2();
+	}
+
+	Size2 s = texture->get_size() * _scale;
+	return Rect2(texture_offset - s / 2.0, s);
+}
+
+void PointLight2D::set_texture(const Ref<Texture2D> &p_texture) {
+	texture = p_texture;
+	if (texture.is_valid()) {
+		RS::get_singleton()->canvas_light_set_texture(_get_light(), texture->get_rid());
+	} else {
+		RS::get_singleton()->canvas_light_set_texture(_get_light(), RID());
+	}
+
+	update_configuration_warning();
+}
+
+Ref<Texture2D> PointLight2D::get_texture() const {
+	return texture;
+}
+
+void PointLight2D::set_texture_offset(const Vector2 &p_offset) {
+	texture_offset = p_offset;
+	RS::get_singleton()->canvas_light_set_texture_offset(_get_light(), texture_offset);
+	item_rect_changed();
+	_change_notify("offset");
+}
+
+Vector2 PointLight2D::get_texture_offset() const {
+	return texture_offset;
+}
+
+String PointLight2D::get_configuration_warning() const {
+	String warning = Node2D::get_configuration_warning();
+
+	if (!texture.is_valid()) {
+		if (!warning.empty()) {
+			warning += "\n\n";
+		}
+		warning += TTR("A texture with the shape of the light must be supplied to the \"Texture\" property.");
+	}
+
+	return warning;
+}
+
+void PointLight2D::set_texture_scale(float p_scale) {
+	_scale = p_scale;
+	// Avoid having 0 scale values, can lead to errors in physics and rendering.
+	if (_scale == 0) {
+		_scale = CMP_EPSILON;
+	}
+	RS::get_singleton()->canvas_light_set_texture_scale(_get_light(), _scale);
+	item_rect_changed();
+}
+
+float PointLight2D::get_texture_scale() const {
+	return _scale;
+}
+
+void PointLight2D::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("set_texture", "texture"), &PointLight2D::set_texture);
+	ClassDB::bind_method(D_METHOD("get_texture"), &PointLight2D::get_texture);
+
+	ClassDB::bind_method(D_METHOD("set_texture_offset", "texture_offset"), &PointLight2D::set_texture_offset);
+	ClassDB::bind_method(D_METHOD("get_texture_offset"), &PointLight2D::get_texture_offset);
+
+	ClassDB::bind_method(D_METHOD("set_texture_scale", "texture_scale"), &PointLight2D::set_texture_scale);
+	ClassDB::bind_method(D_METHOD("get_texture_scale"), &PointLight2D::get_texture_scale);
+
+	ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
+	ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_texture_offset", "get_texture_offset");
+	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "texture_scale", PROPERTY_HINT_RANGE, "0.01,50,0.01"), "set_texture_scale", "get_texture_scale");
+	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0,1024,1,or_greater"), "set_height", "get_height");
+}
+
+PointLight2D::PointLight2D() {
+	RS::get_singleton()->canvas_light_set_mode(_get_light(), RS::CANVAS_LIGHT_MODE_POINT);
+	_scale = 1.0;
+}
+
+PointLight2D::~PointLight2D() {
+}
+
+//////////
+
+void DirectionalLight2D::set_max_distance(float p_distance) {
+	max_distance = p_distance;
+	RS::get_singleton()->canvas_light_set_directional_distance(_get_light(), max_distance);
+}
+float DirectionalLight2D::get_max_distance() const {
+	return max_distance;
+}
+
+void DirectionalLight2D::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("set_max_distance", "pixels"), &DirectionalLight2D::set_max_distance);
+	ClassDB::bind_method(D_METHOD("get_max_distance"), &DirectionalLight2D::get_max_distance);
+
+	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "height", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_height", "get_height");
+	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_RANGE, "0,16384.0,1.0,or_greater"), "set_max_distance", "get_max_distance");
+}
+DirectionalLight2D::DirectionalLight2D() {
+	RS::get_singleton()->canvas_light_set_mode(_get_light(), RS::CANVAS_LIGHT_MODE_DIRECTIONAL);
+	set_max_distance(10000.0);
+}

+ 66 - 35
scene/2d/light_2d.h

@@ -37,13 +37,6 @@ class Light2D : public Node2D {
 	GDCLASS(Light2D, Node2D);
 	GDCLASS(Light2D, Node2D);
 
 
 public:
 public:
-	enum Mode {
-		MODE_ADD,
-		MODE_SUB,
-		MODE_MIX,
-		MODE_MASK,
-	};
-
 	enum ShadowFilter {
 	enum ShadowFilter {
 		SHADOW_FILTER_NONE,
 		SHADOW_FILTER_NONE,
 		SHADOW_FILTER_PCF5,
 		SHADOW_FILTER_PCF5,
@@ -51,6 +44,12 @@ public:
 		SHADOW_FILTER_MAX
 		SHADOW_FILTER_MAX
 	};
 	};
 
 
+	enum BlendMode {
+		BLEND_MODE_ADD,
+		BLEND_MODE_SUB,
+		BLEND_MODE_MIX,
+	};
+
 private:
 private:
 	RID canvas_light;
 	RID canvas_light;
 	bool enabled;
 	bool enabled;
@@ -68,43 +67,25 @@ private:
 	int item_mask;
 	int item_mask;
 	int item_shadow_mask;
 	int item_shadow_mask;
 	float shadow_smooth;
 	float shadow_smooth;
-	Mode mode;
 	Ref<Texture2D> texture;
 	Ref<Texture2D> texture;
 	Vector2 texture_offset;
 	Vector2 texture_offset;
 	ShadowFilter shadow_filter;
 	ShadowFilter shadow_filter;
+	BlendMode blend_mode;
 
 
 	void _update_light_visibility();
 	void _update_light_visibility();
 
 
 protected:
 protected:
+	_FORCE_INLINE_ RID _get_light() const { return canvas_light; }
 	void _notification(int p_what);
 	void _notification(int p_what);
 	static void _bind_methods();
 	static void _bind_methods();
 
 
 public:
 public:
-#ifdef TOOLS_ENABLED
-	virtual Dictionary _edit_get_state() const override;
-	virtual void _edit_set_state(const Dictionary &p_state) override;
-
-	virtual void _edit_set_pivot(const Point2 &p_pivot) override;
-	virtual Point2 _edit_get_pivot() const override;
-	virtual bool _edit_use_pivot() const override;
-	virtual Rect2 _edit_get_rect() const override;
-	virtual bool _edit_use_rect() const override;
-#endif
-
-	virtual Rect2 get_anchorable_rect() const override;
-
 	void set_enabled(bool p_enabled);
 	void set_enabled(bool p_enabled);
 	bool is_enabled() const;
 	bool is_enabled() const;
 
 
 	void set_editor_only(bool p_editor_only);
 	void set_editor_only(bool p_editor_only);
 	bool is_editor_only() const;
 	bool is_editor_only() const;
 
 
-	void set_texture(const Ref<Texture2D> &p_texture);
-	Ref<Texture2D> get_texture() const;
-
-	void set_texture_offset(const Vector2 &p_offset);
-	Vector2 get_texture_offset() const;
-
 	void set_color(const Color &p_color);
 	void set_color(const Color &p_color);
 	Color get_color() const;
 	Color get_color() const;
 
 
@@ -114,9 +95,6 @@ public:
 	void set_energy(float p_energy);
 	void set_energy(float p_energy);
 	float get_energy() const;
 	float get_energy() const;
 
 
-	void set_texture_scale(float p_scale);
-	float get_texture_scale() const;
-
 	void set_z_range_min(int p_min_z);
 	void set_z_range_min(int p_min_z);
 	int get_z_range_min() const;
 	int get_z_range_min() const;
 
 
@@ -135,9 +113,6 @@ public:
 	void set_item_shadow_cull_mask(int p_mask);
 	void set_item_shadow_cull_mask(int p_mask);
 	int get_item_shadow_cull_mask() const;
 	int get_item_shadow_cull_mask() const;
 
 
-	void set_mode(Mode p_mode);
-	Mode get_mode() const;
-
 	void set_shadow_enabled(bool p_enabled);
 	void set_shadow_enabled(bool p_enabled);
 	bool is_shadow_enabled() const;
 	bool is_shadow_enabled() const;
 
 
@@ -150,13 +125,69 @@ public:
 	void set_shadow_smooth(float p_amount);
 	void set_shadow_smooth(float p_amount);
 	float get_shadow_smooth() const;
 	float get_shadow_smooth() const;
 
 
-	String get_configuration_warning() const override;
+	void set_blend_mode(BlendMode p_mode);
+	BlendMode get_blend_mode() const;
 
 
 	Light2D();
 	Light2D();
 	~Light2D();
 	~Light2D();
 };
 };
 
 
-VARIANT_ENUM_CAST(Light2D::Mode);
 VARIANT_ENUM_CAST(Light2D::ShadowFilter);
 VARIANT_ENUM_CAST(Light2D::ShadowFilter);
+VARIANT_ENUM_CAST(Light2D::BlendMode);
+
+class PointLight2D : public Light2D {
+	GDCLASS(PointLight2D, Light2D);
+
+private:
+	float _scale;
+	Ref<Texture2D> texture;
+	Vector2 texture_offset;
+
+protected:
+	static void _bind_methods();
+
+public:
+#ifdef TOOLS_ENABLED
+	virtual Dictionary _edit_get_state() const override;
+	virtual void _edit_set_state(const Dictionary &p_state) override;
+
+	virtual void _edit_set_pivot(const Point2 &p_pivot) override;
+	virtual Point2 _edit_get_pivot() const override;
+	virtual bool _edit_use_pivot() const override;
+	virtual Rect2 _edit_get_rect() const override;
+	virtual bool _edit_use_rect() const override;
+#endif
+
+	virtual Rect2 get_anchorable_rect() const override;
+
+	void set_texture(const Ref<Texture2D> &p_texture);
+	Ref<Texture2D> get_texture() const;
+
+	void set_texture_offset(const Vector2 &p_offset);
+	Vector2 get_texture_offset() const;
+
+	void set_texture_scale(float p_scale);
+	float get_texture_scale() const;
+
+	String get_configuration_warning() const override;
+
+	PointLight2D();
+	~PointLight2D();
+};
+
+class DirectionalLight2D : public Light2D {
+	GDCLASS(DirectionalLight2D, Light2D);
+
+	float max_distance = 10000.0;
+
+protected:
+	static void _bind_methods();
+
+public:
+	void set_max_distance(float p_distance);
+	float get_max_distance() const;
+
+	DirectionalLight2D();
+};
 
 
 #endif // LIGHT_2D_H
 #endif // LIGHT_2D_H

+ 4 - 1
scene/register_scene_types.cpp

@@ -628,7 +628,9 @@ void register_scene_types() {
 	ClassDB::register_class<Polygon2D>();
 	ClassDB::register_class<Polygon2D>();
 	ClassDB::register_class<Skeleton2D>();
 	ClassDB::register_class<Skeleton2D>();
 	ClassDB::register_class<Bone2D>();
 	ClassDB::register_class<Bone2D>();
-	ClassDB::register_class<Light2D>();
+	ClassDB::register_virtual_class<Light2D>();
+	ClassDB::register_class<PointLight2D>();
+	ClassDB::register_class<DirectionalLight2D>();
 	ClassDB::register_class<LightOccluder2D>();
 	ClassDB::register_class<LightOccluder2D>();
 	ClassDB::register_class<OccluderPolygon2D>();
 	ClassDB::register_class<OccluderPolygon2D>();
 	ClassDB::register_class<YSort>();
 	ClassDB::register_class<YSort>();
@@ -917,6 +919,7 @@ void register_scene_types() {
 	ClassDB::add_compatibility_class("VisualShaderNodeScalarUniform", "VisualShaderNodeFloatUniform");
 	ClassDB::add_compatibility_class("VisualShaderNodeScalarUniform", "VisualShaderNodeFloatUniform");
 	ClassDB::add_compatibility_class("World", "World3D");
 	ClassDB::add_compatibility_class("World", "World3D");
 	ClassDB::add_compatibility_class("StreamTexture", "StreamTexture2D");
 	ClassDB::add_compatibility_class("StreamTexture", "StreamTexture2D");
+	ClassDB::add_compatibility_class("Light2D", "PointLight2D");
 
 
 #endif
 #endif
 
 

+ 9 - 4
servers/rendering/rasterizer.h

@@ -832,7 +832,9 @@ public:
 		int layer_max;
 		int layer_max;
 		int item_mask;
 		int item_mask;
 		int item_shadow_mask;
 		int item_shadow_mask;
+		float directional_distance;
 		RS::CanvasLightMode mode;
 		RS::CanvasLightMode mode;
+		RS::CanvasLightBlendMode blend_mode;
 		RID texture;
 		RID texture;
 		Vector2 texture_offset;
 		Vector2 texture_offset;
 		RID canvas;
 		RID canvas;
@@ -854,7 +856,7 @@ public:
 		Light *shadows_next_ptr;
 		Light *shadows_next_ptr;
 		Light *filter_next_ptr;
 		Light *filter_next_ptr;
 		Light *next_ptr;
 		Light *next_ptr;
-		Light *mask_next_ptr;
+		Light *directional_next_ptr;
 
 
 		RID light_internal;
 		RID light_internal;
 		uint64_t version;
 		uint64_t version;
@@ -875,16 +877,18 @@ public:
 			scale = 1.0;
 			scale = 1.0;
 			energy = 1.0;
 			energy = 1.0;
 			item_shadow_mask = 1;
 			item_shadow_mask = 1;
-			mode = RS::CANVAS_LIGHT_MODE_ADD;
+			mode = RS::CANVAS_LIGHT_MODE_POINT;
+			blend_mode = RS::CANVAS_LIGHT_BLEND_MODE_ADD;
 			//			texture_cache = nullptr;
 			//			texture_cache = nullptr;
 			next_ptr = nullptr;
 			next_ptr = nullptr;
-			mask_next_ptr = nullptr;
+			directional_next_ptr = nullptr;
 			filter_next_ptr = nullptr;
 			filter_next_ptr = nullptr;
 			use_shadow = false;
 			use_shadow = false;
 			shadow_buffer_size = 2048;
 			shadow_buffer_size = 2048;
 			shadow_filter = RS::CANVAS_LIGHT_FILTER_NONE;
 			shadow_filter = RS::CANVAS_LIGHT_FILTER_NONE;
 			shadow_smooth = 0.0;
 			shadow_smooth = 0.0;
 			render_index_cache = -1;
 			render_index_cache = -1;
+			directional_distance = 10000.0;
 		}
 		}
 	};
 	};
 
 
@@ -1322,7 +1326,7 @@ public:
 		}
 		}
 	};
 	};
 
 
-	virtual void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel) = 0;
+	virtual void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel) = 0;
 	virtual void canvas_debug_viewport_shadows(Light *p_lights_with_shadow) = 0;
 	virtual void canvas_debug_viewport_shadows(Light *p_lights_with_shadow) = 0;
 
 
 	struct LightOccluderInstance {
 	struct LightOccluderInstance {
@@ -1350,6 +1354,7 @@ public:
 	virtual void light_set_texture(RID p_rid, RID p_texture) = 0;
 	virtual void light_set_texture(RID p_rid, RID p_texture) = 0;
 	virtual void light_set_use_shadow(RID p_rid, bool p_enable) = 0;
 	virtual void light_set_use_shadow(RID p_rid, bool p_enable) = 0;
 	virtual void light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) = 0;
 	virtual void light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) = 0;
+	virtual void light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders) = 0;
 
 
 	virtual RID occluder_polygon_create() = 0;
 	virtual RID occluder_polygon_create() = 0;
 	virtual void occluder_polygon_set_shape_as_lines(RID p_occluder, const Vector<Vector2> &p_lines) = 0;
 	virtual void occluder_polygon_set_shape_as_lines(RID p_occluder, const Vector<Vector2> &p_lines) = 0;

+ 186 - 38
servers/rendering/rasterizer_rd/rasterizer_canvas_rd.cpp

@@ -416,10 +416,6 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_
 
 
 				light_count++;
 				light_count++;
 
 
-				if (light->mode == RS::CANVAS_LIGHT_MODE_MASK) {
-					base_flags |= FLAGS_USING_LIGHT_MASK;
-				}
-
 				if (light_count == MAX_LIGHTS_PER_ITEM) {
 				if (light_count == MAX_LIGHTS_PER_ITEM) {
 					break;
 					break;
 				}
 				}
@@ -430,7 +426,7 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_
 		base_flags |= light_count << FLAGS_LIGHT_COUNT_SHIFT;
 		base_flags |= light_count << FLAGS_LIGHT_COUNT_SHIFT;
 	}
 	}
 
 
-	light_mode = light_count > 0 ? PIPELINE_LIGHT_MODE_ENABLED : PIPELINE_LIGHT_MODE_DISABLED;
+	light_mode = (light_count > 0 || using_directional_lights) ? PIPELINE_LIGHT_MODE_ENABLED : PIPELINE_LIGHT_MODE_DISABLED;
 
 
 	PipelineVariants *pipeline_variants = p_pipeline_variants;
 	PipelineVariants *pipeline_variants = p_pipeline_variants;
 
 
@@ -1194,51 +1190,83 @@ void RasterizerCanvasRD::_render_items(RID p_to_render_target, int p_item_count,
 	RD::get_singleton()->draw_list_end();
 	RD::get_singleton()->draw_list_end();
 }
 }
 
 
-void RasterizerCanvasRD::canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, const Transform2D &p_canvas_transform, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel) {
+void RasterizerCanvasRD::canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_light_list, const Transform2D &p_canvas_transform, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel) {
 	int item_count = 0;
 	int item_count = 0;
 
 
 	//setup canvas state uniforms if needed
 	//setup canvas state uniforms if needed
 
 
 	Transform2D canvas_transform_inverse = p_canvas_transform.affine_inverse();
 	Transform2D canvas_transform_inverse = p_canvas_transform.affine_inverse();
 
 
+	//setup directional lights if exist
+
+	uint32_t light_count = 0;
+	uint32_t directional_light_count = 0;
 	{
 	{
-		//update canvas state uniform buffer
-		State::Buffer state_buffer;
+		Light *l = p_directional_light_list;
+		uint32_t index = 0;
 
 
-		Size2i ssize = storage->render_target_get_size(p_to_render_target);
+		while (l) {
+			if (index == state.max_lights_per_render) {
+				l->render_index_cache = -1;
+				l = l->next_ptr;
+				continue;
+			}
 
 
-		Transform screen_transform;
-		screen_transform.translate(-(ssize.width / 2.0f), -(ssize.height / 2.0f), 0.0f);
-		screen_transform.scale(Vector3(2.0f / ssize.width, 2.0f / ssize.height, 1.0f));
-		_update_transform_to_mat4(screen_transform, state_buffer.screen_transform);
-		_update_transform_2d_to_mat4(p_canvas_transform, state_buffer.canvas_transform);
+			CanvasLight *clight = canvas_light_owner.getornull(l->light_internal);
+			if (!clight) { //unused or invalid texture
+				l->render_index_cache = -1;
+				l = l->next_ptr;
+				ERR_CONTINUE(!clight);
+			}
 
 
-		Transform2D normal_transform = p_canvas_transform;
-		normal_transform.elements[0].normalize();
-		normal_transform.elements[1].normalize();
-		normal_transform.elements[2] = Vector2();
-		_update_transform_2d_to_mat4(normal_transform, state_buffer.canvas_normal_transform);
+			Vector2 canvas_light_dir = l->xform_cache.elements[1].normalized();
 
 
-		state_buffer.canvas_modulate[0] = p_modulate.r;
-		state_buffer.canvas_modulate[1] = p_modulate.g;
-		state_buffer.canvas_modulate[2] = p_modulate.b;
-		state_buffer.canvas_modulate[3] = p_modulate.a;
+			state.light_uniforms[index].position[0] = -canvas_light_dir.x;
+			state.light_uniforms[index].position[1] = -canvas_light_dir.y;
 
 
-		Size2 render_target_size = storage->render_target_get_size(p_to_render_target);
-		state_buffer.screen_pixel_size[0] = 1.0 / render_target_size.x;
-		state_buffer.screen_pixel_size[1] = 1.0 / render_target_size.y;
+			_update_transform_2d_to_mat2x4(clight->shadow.directional_xform, state.light_uniforms[index].shadow_matrix);
 
 
-		state_buffer.time = state.time;
-		state_buffer.use_pixel_snap = p_snap_2d_vertices_to_pixel;
+			state.light_uniforms[index].height = l->height; //0..1 here
 
 
-		RD::get_singleton()->buffer_update(state.canvas_state_buffer, 0, sizeof(State::Buffer), &state_buffer, true);
+			for (int i = 0; i < 4; i++) {
+				state.light_uniforms[index].shadow_color[i] = uint8_t(CLAMP(int32_t(l->shadow_color[i] * 255.0), 0, 255));
+				state.light_uniforms[index].color[i] = l->color[i];
+			}
+
+			state.light_uniforms[index].color[3] = l->energy; //use alpha for energy, so base color can go separate
+
+			if (state.shadow_fb.is_valid()) {
+				state.light_uniforms[index].shadow_pixel_size = (1.0 / state.shadow_texture_size) * (1.0 + l->shadow_smooth);
+				state.light_uniforms[index].shadow_z_far_inv = 1.0 / clight->shadow.z_far;
+				state.light_uniforms[index].shadow_y_ofs = clight->shadow.y_offset;
+			} else {
+				state.light_uniforms[index].shadow_pixel_size = 1.0;
+				state.light_uniforms[index].shadow_z_far_inv = 1.0;
+				state.light_uniforms[index].shadow_y_ofs = 0;
+			}
+
+			state.light_uniforms[index].flags = l->blend_mode << LIGHT_FLAGS_BLEND_SHIFT;
+			state.light_uniforms[index].flags |= l->shadow_filter << LIGHT_FLAGS_FILTER_SHIFT;
+			if (clight->shadow.enabled) {
+				state.light_uniforms[index].flags |= LIGHT_FLAGS_HAS_SHADOW;
+			}
+
+			l->render_index_cache = index;
+
+			index++;
+			l = l->next_ptr;
+		}
+
+		light_count = index;
+		directional_light_count = light_count;
+		using_directional_lights = directional_light_count > 0;
 	}
 	}
 
 
 	//setup lights if exist
 	//setup lights if exist
 
 
 	{
 	{
 		Light *l = p_light_list;
 		Light *l = p_light_list;
-		uint32_t index = 0;
+		uint32_t index = light_count;
 
 
 		while (l) {
 		while (l) {
 			if (index == state.max_lights_per_render) {
 			if (index == state.max_lights_per_render) {
@@ -1280,7 +1308,7 @@ void RasterizerCanvasRD::canvas_render_items(RID p_to_render_target, Item *p_ite
 				state.light_uniforms[index].shadow_y_ofs = 0;
 				state.light_uniforms[index].shadow_y_ofs = 0;
 			}
 			}
 
 
-			state.light_uniforms[index].flags |= l->mode << LIGHT_FLAGS_BLEND_SHIFT;
+			state.light_uniforms[index].flags = l->blend_mode << LIGHT_FLAGS_BLEND_SHIFT;
 			state.light_uniforms[index].flags |= l->shadow_filter << LIGHT_FLAGS_FILTER_SHIFT;
 			state.light_uniforms[index].flags |= l->shadow_filter << LIGHT_FLAGS_FILTER_SHIFT;
 			if (clight->shadow.enabled) {
 			if (clight->shadow.enabled) {
 				state.light_uniforms[index].flags |= LIGHT_FLAGS_HAS_SHADOW;
 				state.light_uniforms[index].flags |= LIGHT_FLAGS_HAS_SHADOW;
@@ -1306,9 +1334,46 @@ void RasterizerCanvasRD::canvas_render_items(RID p_to_render_target, Item *p_ite
 			l = l->next_ptr;
 			l = l->next_ptr;
 		}
 		}
 
 
-		if (index > 0) {
-			RD::get_singleton()->buffer_update(state.lights_uniform_buffer, 0, sizeof(LightUniform) * index, &state.light_uniforms[0], true);
-		}
+		light_count = index;
+	}
+
+	if (light_count > 0) {
+		RD::get_singleton()->buffer_update(state.lights_uniform_buffer, 0, sizeof(LightUniform) * light_count, &state.light_uniforms[0], true);
+	}
+
+	{
+		//update canvas state uniform buffer
+		State::Buffer state_buffer;
+
+		Size2i ssize = storage->render_target_get_size(p_to_render_target);
+
+		Transform screen_transform;
+		screen_transform.translate(-(ssize.width / 2.0f), -(ssize.height / 2.0f), 0.0f);
+		screen_transform.scale(Vector3(2.0f / ssize.width, 2.0f / ssize.height, 1.0f));
+		_update_transform_to_mat4(screen_transform, state_buffer.screen_transform);
+		_update_transform_2d_to_mat4(p_canvas_transform, state_buffer.canvas_transform);
+
+		Transform2D normal_transform = p_canvas_transform;
+		normal_transform.elements[0].normalize();
+		normal_transform.elements[1].normalize();
+		normal_transform.elements[2] = Vector2();
+		_update_transform_2d_to_mat4(normal_transform, state_buffer.canvas_normal_transform);
+
+		state_buffer.canvas_modulate[0] = p_modulate.r;
+		state_buffer.canvas_modulate[1] = p_modulate.g;
+		state_buffer.canvas_modulate[2] = p_modulate.b;
+		state_buffer.canvas_modulate[3] = p_modulate.a;
+
+		Size2 render_target_size = storage->render_target_get_size(p_to_render_target);
+		state_buffer.screen_pixel_size[0] = 1.0 / render_target_size.x;
+		state_buffer.screen_pixel_size[1] = 1.0 / render_target_size.y;
+
+		state_buffer.time = state.time;
+		state_buffer.use_pixel_snap = p_snap_2d_vertices_to_pixel;
+
+		state_buffer.directional_light_count = directional_light_count;
+
+		RD::get_singleton()->buffer_update(state.canvas_state_buffer, 0, sizeof(State::Buffer), &state_buffer, true);
 	}
 	}
 
 
 	{ //default filter/repeat
 	{ //default filter/repeat
@@ -1439,10 +1504,7 @@ void RasterizerCanvasRD::light_set_use_shadow(RID p_rid, bool p_enable) {
 	cl->shadow.enabled = p_enable;
 	cl->shadow.enabled = p_enable;
 }
 }
 
 
-void RasterizerCanvasRD::light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) {
-	CanvasLight *cl = canvas_light_owner.getornull(p_rid);
-	ERR_FAIL_COND(!cl->shadow.enabled);
-
+void RasterizerCanvasRD::_update_shadow_atlas() {
 	if (state.shadow_fb == RID()) {
 	if (state.shadow_fb == RID()) {
 		//ah, we lack the shadow texture..
 		//ah, we lack the shadow texture..
 		RD::get_singleton()->free(state.shadow_texture); //erase placeholder
 		RD::get_singleton()->free(state.shadow_texture); //erase placeholder
@@ -1474,6 +1536,12 @@ void RasterizerCanvasRD::light_update_shadow(RID p_rid, int p_shadow_index, cons
 
 
 		state.shadow_fb = RD::get_singleton()->framebuffer_create(fb_textures);
 		state.shadow_fb = RD::get_singleton()->framebuffer_create(fb_textures);
 	}
 	}
+}
+void RasterizerCanvasRD::light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders) {
+	CanvasLight *cl = canvas_light_owner.getornull(p_rid);
+	ERR_FAIL_COND(!cl->shadow.enabled);
+
+	_update_shadow_atlas();
 
 
 	cl->shadow.z_far = p_far;
 	cl->shadow.z_far = p_far;
 	cl->shadow.y_offset = float(p_shadow_index * 2 + 1) / float(state.max_lights_per_render * 2);
 	cl->shadow.y_offset = float(p_shadow_index * 2 + 1) / float(state.max_lights_per_render * 2);
@@ -1547,6 +1615,86 @@ void RasterizerCanvasRD::light_update_shadow(RID p_rid, int p_shadow_index, cons
 	}
 	}
 }
 }
 
 
+void RasterizerCanvasRD::light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders) {
+	CanvasLight *cl = canvas_light_owner.getornull(p_rid);
+	ERR_FAIL_COND(!cl->shadow.enabled);
+
+	_update_shadow_atlas();
+
+	Vector2 light_dir = p_light_xform.elements[1].normalized();
+
+	Vector2 center = p_clip_rect.position + p_clip_rect.size * 0.5;
+
+	float to_edge_distance = ABS(light_dir.dot(p_clip_rect.get_support(light_dir)) - light_dir.dot(center));
+
+	Vector2 from_pos = center - light_dir * (to_edge_distance + p_cull_distance);
+	float distance = to_edge_distance * 2.0 + p_cull_distance;
+	float half_size = p_clip_rect.size.length() * 0.5; //shadow length, must keep this no matter the angle
+
+	cl->shadow.z_far = distance;
+	cl->shadow.y_offset = float(p_shadow_index * 2 + 1) / float(state.max_lights_per_render * 2);
+
+	Transform2D to_light_xform;
+
+	to_light_xform[2] = from_pos;
+	to_light_xform[1] = light_dir;
+	to_light_xform[0] = -light_dir.tangent();
+
+	to_light_xform.invert();
+
+	Vector<Color> cc;
+	cc.push_back(Color(1, 1, 1, 1));
+
+	Rect2i rect(0, p_shadow_index * 2, state.shadow_texture_size, 2);
+	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(state.shadow_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, cc, 1.0, 0, rect);
+
+	CameraMatrix projection;
+	projection.set_orthogonal(-half_size, half_size, -0.5, 0.5, 0.0, distance);
+	projection = projection * CameraMatrix(Transform().looking_at(Vector3(0, 1, 0), Vector3(0, 0, -1)).affine_inverse());
+
+	ShadowRenderPushConstant push_constant;
+	for (int y = 0; y < 4; y++) {
+		for (int x = 0; x < 4; x++) {
+			push_constant.projection[y * 4 + x] = projection.matrix[y][x];
+		}
+	}
+
+	push_constant.direction[0] = 0.0;
+	push_constant.direction[1] = 1.0;
+	push_constant.z_far = distance;
+	push_constant.pad = 0;
+
+	LightOccluderInstance *instance = p_occluders;
+
+	while (instance) {
+		OccluderPolygon *co = occluder_polygon_owner.getornull(instance->occluder);
+
+		if (!co || co->index_array.is_null() || !(p_light_mask & instance->light_mask)) {
+			instance = instance->next;
+			continue;
+		}
+
+		_update_transform_2d_to_mat2x4(to_light_xform * instance->xform_cache, push_constant.modelview);
+
+		RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, shadow_render.render_pipelines[co->cull_mode]);
+		RD::get_singleton()->draw_list_bind_vertex_array(draw_list, co->vertex_array);
+		RD::get_singleton()->draw_list_bind_index_array(draw_list, co->index_array);
+		RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(ShadowRenderPushConstant));
+
+		RD::get_singleton()->draw_list_draw(draw_list, true);
+
+		instance = instance->next;
+	}
+
+	RD::get_singleton()->draw_list_end();
+
+	Transform2D to_shadow;
+	to_shadow.elements[0].x = 1.0 / -(half_size * 2.0);
+	to_shadow.elements[2].x = 0.5;
+
+	cl->shadow.directional_xform = to_shadow * to_light_xform;
+}
+
 RID RasterizerCanvasRD::occluder_polygon_create() {
 RID RasterizerCanvasRD::occluder_polygon_create() {
 	OccluderPolygon occluder;
 	OccluderPolygon occluder;
 	occluder.point_count = 0;
 	occluder.point_count = 0;

+ 10 - 4
servers/rendering/rasterizer_rd/rasterizer_canvas_rd.h

@@ -75,7 +75,6 @@ class RasterizerCanvasRD : public RasterizerCanvas {
 
 
 		FLAGS_CLIP_RECT_UV = (1 << 9),
 		FLAGS_CLIP_RECT_UV = (1 << 9),
 		FLAGS_TRANSPOSE_RECT = (1 << 10),
 		FLAGS_TRANSPOSE_RECT = (1 << 10),
-		FLAGS_USING_LIGHT_MASK = (1 << 11),
 
 
 		FLAGS_NINEPACH_DRAW_CENTER = (1 << 12),
 		FLAGS_NINEPACH_DRAW_CENTER = (1 << 12),
 		FLAGS_USING_PARTICLES = (1 << 13),
 		FLAGS_USING_PARTICLES = (1 << 13),
@@ -269,6 +268,7 @@ class RasterizerCanvasRD : public RasterizerCanvas {
 			bool enabled = false;
 			bool enabled = false;
 			float z_far;
 			float z_far;
 			float y_offset;
 			float y_offset;
+			Transform2D directional_xform;
 		} shadow;
 		} shadow;
 	};
 	};
 
 
@@ -331,12 +331,13 @@ class RasterizerCanvasRD : public RasterizerCanvas {
 			float screen_transform[16];
 			float screen_transform[16];
 			float canvas_normal_transform[16];
 			float canvas_normal_transform[16];
 			float canvas_modulate[4];
 			float canvas_modulate[4];
+
 			float screen_pixel_size[2];
 			float screen_pixel_size[2];
 			float time;
 			float time;
 			uint32_t use_pixel_snap;
 			uint32_t use_pixel_snap;
 
 
-			//uint32_t light_count;
-			//uint32_t pad[3];
+			uint32_t directional_light_count;
+			uint32_t pad[3];
 		};
 		};
 
 
 		LightUniform *light_uniforms;
 		LightUniform *light_uniforms;
@@ -355,6 +356,7 @@ class RasterizerCanvasRD : public RasterizerCanvas {
 		uint32_t max_lights_per_item;
 		uint32_t max_lights_per_item;
 
 
 		double time;
 		double time;
+
 	} state;
 	} state;
 
 
 	struct PushConstant {
 	struct PushConstant {
@@ -388,6 +390,7 @@ class RasterizerCanvasRD : public RasterizerCanvas {
 
 
 	Item *items[MAX_RENDER_ITEMS];
 	Item *items[MAX_RENDER_ITEMS];
 
 
+	bool using_directional_lights = false;
 	RID default_canvas_texture;
 	RID default_canvas_texture;
 
 
 	RID default_canvas_group_shader;
 	RID default_canvas_group_shader;
@@ -408,6 +411,8 @@ class RasterizerCanvasRD : public RasterizerCanvas {
 	_FORCE_INLINE_ void _update_transform_2d_to_mat4(const Transform2D &p_transform, float *p_mat4);
 	_FORCE_INLINE_ void _update_transform_2d_to_mat4(const Transform2D &p_transform, float *p_mat4);
 	_FORCE_INLINE_ void _update_transform_to_mat4(const Transform &p_transform, float *p_mat4);
 	_FORCE_INLINE_ void _update_transform_to_mat4(const Transform &p_transform, float *p_mat4);
 
 
+	void _update_shadow_atlas();
+
 public:
 public:
 	PolygonID request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>());
 	PolygonID request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>());
 	void free_polygon(PolygonID p_polygon);
 	void free_polygon(PolygonID p_polygon);
@@ -416,12 +421,13 @@ public:
 	void light_set_texture(RID p_rid, RID p_texture);
 	void light_set_texture(RID p_rid, RID p_texture);
 	void light_set_use_shadow(RID p_rid, bool p_enable);
 	void light_set_use_shadow(RID p_rid, bool p_enable);
 	void light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders);
 	void light_update_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders);
+	void light_update_directional_shadow(RID p_rid, int p_shadow_index, const Transform2D &p_light_xform, int p_light_mask, float p_cull_distance, const Rect2 &p_clip_rect, LightOccluderInstance *p_occluders);
 
 
 	RID occluder_polygon_create();
 	RID occluder_polygon_create();
 	void occluder_polygon_set_shape_as_lines(RID p_occluder, const Vector<Vector2> &p_lines);
 	void occluder_polygon_set_shape_as_lines(RID p_occluder, const Vector<Vector2> &p_lines);
 	void occluder_polygon_set_cull_mode(RID p_occluder, RS::CanvasOccluderPolygonCullMode p_mode);
 	void occluder_polygon_set_cull_mode(RID p_occluder, RS::CanvasOccluderPolygonCullMode p_mode);
 
 
-	void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel);
+	void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_light_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel);
 
 
 	void canvas_debug_viewport_shadows(Light *p_lights_with_shadow) {}
 	void canvas_debug_viewport_shadows(Light *p_lights_with_shadow) {}
 
 

+ 144 - 80
servers/rendering/rasterizer_rd/shaders/canvas.glsl

@@ -249,7 +249,7 @@ vec4 light_compute(
 		inout vec4 shadow_modulate,
 		inout vec4 shadow_modulate,
 		vec2 screen_uv,
 		vec2 screen_uv,
 		vec2 uv,
 		vec2 uv,
-		vec4 color) {
+		vec4 color, bool is_directional) {
 	vec4 light = vec4(0.0);
 	vec4 light = vec4(0.0);
 	/* clang-format off */
 	/* clang-format off */
 LIGHT_SHADER_CODE
 LIGHT_SHADER_CODE
@@ -302,6 +302,99 @@ float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, flo
 
 
 #endif
 #endif
 
 
+#ifdef USE_LIGHTING
+
+vec3 light_normal_compute(vec3 light_vec, vec3 normal, vec3 base_color, vec3 light_color, vec4 specular_shininess, bool specular_shininess_used) {
+	float cNdotL = max(0.0, dot(normal, light_vec));
+
+	if (specular_shininess_used) {
+		//blinn
+		vec3 view = vec3(0.0, 0.0, 1.0); // not great but good enough
+		vec3 half_vec = normalize(view + light_vec);
+
+		float cNdotV = max(dot(normal, view), 0.0);
+		float cNdotH = max(dot(normal, half_vec), 0.0);
+		float cVdotH = max(dot(view, half_vec), 0.0);
+		float cLdotH = max(dot(light_vec, half_vec), 0.0);
+		float shininess = exp2(15.0 * specular_shininess.a + 1.0) * 0.25;
+		float blinn = pow(cNdotH, shininess);
+		blinn *= (shininess + 8.0) * (1.0 / (8.0 * M_PI));
+		float s = (blinn) / max(4.0 * cNdotV * cNdotL, 0.75);
+
+		return specular_shininess.rgb * light_color * s + light_color * base_color * cNdotL;
+	} else {
+		return light_color * base_color * cNdotL;
+	}
+}
+
+//float distance = length(shadow_pos);
+vec4 light_shadow_compute(uint light_base, vec4 light_color, vec4 shadow_uv
+#ifdef LIGHT_SHADER_CODE_USED
+		,
+		vec3 shadow_modulate
+#endif
+) {
+	float shadow;
+	uint shadow_mode = light_array.data[light_base].flags & LIGHT_FLAGS_FILTER_MASK;
+
+	if (shadow_mode == LIGHT_FLAGS_SHADOW_NEAREST) {
+		shadow = textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv, 0.0).x;
+	} else if (shadow_mode == LIGHT_FLAGS_SHADOW_PCF5) {
+		vec4 shadow_pixel_size = vec4(light_array.data[light_base].shadow_pixel_size, 0.0, 0.0, 0.0);
+		shadow = 0.0;
+		shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 2.0, 0.0).x;
+		shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size, 0.0).x;
+		shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv, 0.0).x;
+		shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size, 0.0).x;
+		shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 2.0, 0.0).x;
+		shadow /= 5.0;
+	} else { //PCF13
+		vec4 shadow_pixel_size = vec4(light_array.data[light_base].shadow_pixel_size, 0.0, 0.0, 0.0);
+		shadow = 0.0;
+		shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 6.0, 0.0).x;
+		shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 5.0, 0.0).x;
+		shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 4.0, 0.0).x;
+		shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 3.0, 0.0).x;
+		shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 2.0, 0.0).x;
+		shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size, 0.0).x;
+		shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv, 0.0).x;
+		shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size, 0.0).x;
+		shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 2.0, 0.0).x;
+		shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 3.0, 0.0).x;
+		shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 4.0, 0.0).x;
+		shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 5.0, 0.0).x;
+		shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 6.0, 0.0).x;
+		shadow /= 13.0;
+	}
+
+	vec4 shadow_color = unpackUnorm4x8(light_array.data[light_base].shadow_color);
+#ifdef LIGHT_SHADER_CODE_USED
+	shadow_color *= shadow_modulate;
+#endif
+
+	shadow_color.a *= light_color.a; //respect light alpha
+
+	return mix(light_color, shadow_color, shadow);
+}
+
+void light_blend_compute(uint light_base, vec4 light_color, inout vec3 color) {
+	uint blend_mode = light_array.data[light_base].flags & LIGHT_FLAGS_BLEND_MASK;
+
+	switch (blend_mode) {
+		case LIGHT_FLAGS_BLEND_MODE_ADD: {
+			color.rgb += light_color.rgb * light_color.a;
+		} break;
+		case LIGHT_FLAGS_BLEND_MODE_SUB: {
+			color.rgb -= light_color.rgb * light_color.a;
+		} break;
+		case LIGHT_FLAGS_BLEND_MODE_MIX: {
+			color.rgb = mix(color.rgb, light_color.rgb, light_color.a);
+		} break;
+	}
+}
+
+#endif
+
 void main() {
 void main() {
 	vec4 color = color_interp;
 	vec4 color = color_interp;
 	vec2 uv = uv_interp;
 	vec2 uv = uv_interp;
@@ -332,6 +425,7 @@ void main() {
 	color *= texture(sampler2D(color_texture, texture_sampler), uv);
 	color *= texture(sampler2D(color_texture, texture_sampler), uv);
 
 
 	uint light_count = (draw_data.flags >> FLAGS_LIGHT_COUNT_SHIFT) & 0xF; //max 16 lights
 	uint light_count = (draw_data.flags >> FLAGS_LIGHT_COUNT_SHIFT) & 0xF; //max 16 lights
+	bool using_light = light_count > 0 || canvas_data.directional_light_count > 0;
 
 
 	vec3 normal;
 	vec3 normal;
 
 
@@ -341,7 +435,7 @@ void main() {
 	bool normal_used = false;
 	bool normal_used = false;
 #endif
 #endif
 
 
-	if (normal_used || (light_count > 0 && bool(draw_data.flags & FLAGS_DEFAULT_NORMAL_MAP_USED))) {
+	if (normal_used || (using_light && bool(draw_data.flags & FLAGS_DEFAULT_NORMAL_MAP_USED))) {
 		normal.xy = texture(sampler2D(normal_texture, texture_sampler), uv).xy * vec2(2.0, -2.0) - vec2(1.0, -1.0);
 		normal.xy = texture(sampler2D(normal_texture, texture_sampler), uv).xy * vec2(2.0, -2.0) - vec2(1.0, -1.0);
 		normal.z = sqrt(1.0 - dot(normal.xy, normal.xy));
 		normal.z = sqrt(1.0 - dot(normal.xy, normal.xy));
 		normal_used = true;
 		normal_used = true;
@@ -358,7 +452,7 @@ void main() {
 	bool specular_shininess_used = false;
 	bool specular_shininess_used = false;
 #endif
 #endif
 
 
-	if (specular_shininess_used || (light_count > 0 && normal_used && bool(draw_data.flags & FLAGS_DEFAULT_SPECULAR_MAP_USED))) {
+	if (specular_shininess_used || (using_light && normal_used && bool(draw_data.flags & FLAGS_DEFAULT_SPECULAR_MAP_USED))) {
 		specular_shininess = texture(sampler2D(specular_texture, texture_sampler), uv);
 		specular_shininess = texture(sampler2D(specular_texture, texture_sampler), uv);
 		specular_shininess *= unpackUnorm4x8(draw_data.specular_shininess);
 		specular_shininess *= unpackUnorm4x8(draw_data.specular_shininess);
 		specular_shininess_used = true;
 		specular_shininess_used = true;
@@ -401,13 +495,52 @@ FRAGMENT_SHADER_CODE
 		normal = normalize((canvas_data.canvas_normal_transform * vec4(normal, 0.0)).xyz);
 		normal = normalize((canvas_data.canvas_normal_transform * vec4(normal, 0.0)).xyz);
 	}
 	}
 
 
-	vec4 base_color = color;
+	vec3 base_color = color.rgb;
 	if (bool(draw_data.flags & FLAGS_USING_LIGHT_MASK)) {
 	if (bool(draw_data.flags & FLAGS_USING_LIGHT_MASK)) {
 		color = vec4(0.0); //invisible by default due to using light mask
 		color = vec4(0.0); //invisible by default due to using light mask
 	}
 	}
 
 
 	color *= canvas_data.canvas_modulation;
 	color *= canvas_data.canvas_modulation;
 #ifdef USE_LIGHTING
 #ifdef USE_LIGHTING
+
+	// Directional Lights
+
+	for (uint i = 0; i < canvas_data.directional_light_count; i++) {
+		uint light_base = i;
+
+		vec2 direction = light_array.data[light_base].position;
+		vec4 light_color = light_array.data[light_base].color;
+
+#ifdef LIGHT_SHADER_CODE_USED
+
+		vec4 shadow_modulate = vec4(1.0);
+		light_color = light_compute(light_vertex, direction, normal, light_color, light_color.a, specular_shininess, shadow_modulate, screen_uv, color, uv, true);
+#else
+
+		if (normal_used) {
+			vec3 light_vec = normalize(mix(vec3(direction, 0.0), vec3(0, 0, 1), light_array.data[light_base].height));
+			light_color.rgb = light_normal_compute(light_vec, normal, base_color, light_color.rgb, specular_shininess, specular_shininess_used);
+		}
+#endif
+
+		if (bool(light_array.data[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) {
+			vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array.data[light_base].shadow_matrix[0], light_array.data[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations.
+
+			vec4 shadow_uv = vec4(shadow_pos.x, light_array.data[light_base].shadow_y_ofs, shadow_pos.y * light_array.data[light_base].shadow_zfar_inv, 1.0);
+
+			light_color = light_shadow_compute(light_base, light_color, shadow_uv
+#ifdef LIGHT_SHADER_CODE_USED
+					,
+					shadow_modulate
+#endif
+			);
+		}
+
+		light_blend_compute(light_base, light_color, color.rgb);
+	}
+
+	// Positional Lights
+
 	for (uint i = 0; i < MAX_LIGHTS_PER_ITEM; i++) {
 	for (uint i = 0; i < MAX_LIGHTS_PER_ITEM; i++) {
 		if (i >= light_count) {
 		if (i >= light_count) {
 			break;
 			break;
@@ -440,7 +573,7 @@ FRAGMENT_SHADER_CODE
 		vec3 light_position = vec3(light_array.data[light_base].position, light_array.data[light_base].height);
 		vec3 light_position = vec3(light_array.data[light_base].position, light_array.data[light_base].height);
 
 
 		light_color.rgb *= light_base_color.rgb;
 		light_color.rgb *= light_base_color.rgb;
-		light_color = light_compute(light_vertex, light_position, normal, light_color, light_base_color.a, specular_shininess, shadow_modulate, screen_uv, color, uv);
+		light_color = light_compute(light_vertex, light_position, normal, light_color, light_base_color.a, specular_shininess, shadow_modulate, screen_uv, color, uv, false);
 #else
 #else
 
 
 		light_color.rgb *= light_base_color.rgb * light_base_color.a;
 		light_color.rgb *= light_base_color.rgb * light_base_color.a;
@@ -451,24 +584,7 @@ FRAGMENT_SHADER_CODE
 			vec3 light_vec = normalize(light_pos - pos);
 			vec3 light_vec = normalize(light_pos - pos);
 			float cNdotL = max(0.0, dot(normal, light_vec));
 			float cNdotL = max(0.0, dot(normal, light_vec));
 
 
-			if (specular_shininess_used) {
-				//blinn
-				vec3 view = vec3(0.0, 0.0, 1.0); // not great but good enough
-				vec3 half_vec = normalize(view + light_vec);
-
-				float cNdotV = max(dot(normal, view), 0.0);
-				float cNdotH = max(dot(normal, half_vec), 0.0);
-				float cVdotH = max(dot(view, half_vec), 0.0);
-				float cLdotH = max(dot(light_vec, half_vec), 0.0);
-				float shininess = exp2(15.0 * specular_shininess.a + 1.0) * 0.25;
-				float blinn = pow(cNdotH, shininess);
-				blinn *= (shininess + 8.0) * (1.0 / (8.0 * M_PI));
-				float s = (blinn) / max(4.0 * cNdotV * cNdotL, 0.75);
-
-				light_color.rgb = specular_shininess.rgb * light_base_color.rgb * s + light_color.rgb * cNdotL;
-			} else {
-				light_color.rgb *= cNdotL;
-			}
+			light_color.rgb = light_normal_compute(light_vec, normal, base_color, light_color.rgb, specular_shininess, specular_shininess_used);
 		}
 		}
 #endif
 #endif
 		if (any(lessThan(tex_uv, vec2(0.0, 0.0))) || any(greaterThanEqual(tex_uv, vec2(1.0, 1.0)))) {
 		if (any(lessThan(tex_uv, vec2(0.0, 0.0))) || any(greaterThanEqual(tex_uv, vec2(1.0, 1.0)))) {
@@ -506,69 +622,17 @@ FRAGMENT_SHADER_CODE
 			distance *= light_array.data[light_base].shadow_zfar_inv;
 			distance *= light_array.data[light_base].shadow_zfar_inv;
 
 
 			//float distance = length(shadow_pos);
 			//float distance = length(shadow_pos);
-			float shadow;
-			uint shadow_mode = light_array.data[light_base].flags & LIGHT_FLAGS_FILTER_MASK;
-
 			vec4 shadow_uv = vec4(tex_ofs, light_array.data[light_base].shadow_y_ofs, distance, 1.0);
 			vec4 shadow_uv = vec4(tex_ofs, light_array.data[light_base].shadow_y_ofs, distance, 1.0);
 
 
-			if (shadow_mode == LIGHT_FLAGS_SHADOW_NEAREST) {
-				shadow = textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv, 0.0).x;
-			} else if (shadow_mode == LIGHT_FLAGS_SHADOW_PCF5) {
-				vec4 shadow_pixel_size = vec4(light_array.data[light_base].shadow_pixel_size, 0.0, 0.0, 0.0);
-				shadow = 0.0;
-				shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 2.0, 0.0).x;
-				shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size, 0.0).x;
-				shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv, 0.0).x;
-				shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size, 0.0).x;
-				shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 2.0, 0.0).x;
-				shadow /= 5.0;
-			} else { //PCF13
-				vec4 shadow_pixel_size = vec4(light_array.data[light_base].shadow_pixel_size, 0.0, 0.0, 0.0);
-				shadow = 0.0;
-				shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 6.0, 0.0).x;
-				shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 5.0, 0.0).x;
-				shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 4.0, 0.0).x;
-				shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 3.0, 0.0).x;
-				shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size * 2.0, 0.0).x;
-				shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv - shadow_pixel_size, 0.0).x;
-				shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv, 0.0).x;
-				shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size, 0.0).x;
-				shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 2.0, 0.0).x;
-				shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 3.0, 0.0).x;
-				shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 4.0, 0.0).x;
-				shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 5.0, 0.0).x;
-				shadow += textureProjLod(sampler2DShadow(shadow_atlas_texture, shadow_sampler), shadow_uv + shadow_pixel_size * 6.0, 0.0).x;
-				shadow /= 13.0;
-			}
-
-			vec4 shadow_color = unpackUnorm4x8(light_array.data[light_base].shadow_color);
+			light_color = light_shadow_compute(light_base, light_color, shadow_uv
 #ifdef LIGHT_SHADER_CODE_USED
 #ifdef LIGHT_SHADER_CODE_USED
-			shadow_color *= shadow_modulate;
+					,
+					shadow_modulate
 #endif
 #endif
-
-			shadow_color.a *= light_color.a; //respect light alpha
-
-			light_color = mix(light_color, shadow_color, shadow);
-			//light_color = mix(light_color, shadow_color, shadow);
+			);
 		}
 		}
 
 
-		uint blend_mode = light_array.data[light_base].flags & LIGHT_FLAGS_BLEND_MASK;
-
-		switch (blend_mode) {
-			case LIGHT_FLAGS_BLEND_MODE_ADD: {
-				color.rgb += light_color.rgb * light_color.a;
-			} break;
-			case LIGHT_FLAGS_BLEND_MODE_SUB: {
-				color.rgb -= light_color.rgb * light_color.a;
-			} break;
-			case LIGHT_FLAGS_BLEND_MODE_MIX: {
-				color.rgb = mix(color.rgb, light_color.rgb, light_color.a);
-			} break;
-			case LIGHT_FLAGS_BLEND_MODE_MASK: {
-				light_color.a *= base_color.a;
-				color.rgb = mix(color.rgb, light_color.rgb, light_color.a);
-			} break;
-		}
+		light_blend_compute(light_base, light_color, color.rgb);
 	}
 	}
 #endif
 #endif
 
 

+ 4 - 1
servers/rendering/rasterizer_rd/shaders/canvas_uniforms_inc.glsl

@@ -68,7 +68,10 @@ layout(set = 0, binding = 1, std140) uniform CanvasData {
 	float time;
 	float time;
 	bool use_pixel_snap;
 	bool use_pixel_snap;
 
 
-	//uint light_count;
+	uint directional_light_count;
+	uint pad0;
+	uint pad1;
+	uint pad2;
 }
 }
 canvas_data;
 canvas_data;
 
 

+ 49 - 34
servers/rendering/rendering_server_canvas.cpp

@@ -37,7 +37,7 @@
 
 
 static const int z_range = RS::CANVAS_ITEM_Z_MAX - RS::CANVAS_ITEM_Z_MIN + 1;
 static const int z_range = RS::CANVAS_ITEM_Z_MAX - RS::CANVAS_ITEM_Z_MIN + 1;
 
 
-void RenderingServerCanvas::_render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RasterizerCanvas::Light *p_lights, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel) {
+void RenderingServerCanvas::_render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RasterizerCanvas::Light *p_lights, RasterizerCanvas::Light *p_directional_lights, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel) {
 	RENDER_TIMESTAMP("Cull CanvasItem Tree");
 	RENDER_TIMESTAMP("Cull CanvasItem Tree");
 
 
 	memset(z_list, 0, z_range * sizeof(RasterizerCanvas::Item *));
 	memset(z_list, 0, z_range * sizeof(RasterizerCanvas::Item *));
@@ -68,7 +68,7 @@ void RenderingServerCanvas::_render_canvas_item_tree(RID p_to_render_target, Can
 
 
 	RENDER_TIMESTAMP("Render Canvas Items");
 	RENDER_TIMESTAMP("Render Canvas Items");
 
 
-	RSG::canvas_render->canvas_render_items(p_to_render_target, list, p_modulate, p_lights, p_transform, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel);
+	RSG::canvas_render->canvas_render_items(p_to_render_target, list, p_modulate, p_lights, p_directional_lights, p_transform, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel);
 }
 }
 
 
 void _collect_ysort_children(RenderingServerCanvas::Item *p_canvas_item, Transform2D p_transform, RenderingServerCanvas::Item *p_material_owner, RenderingServerCanvas::Item **r_items, int &r_index) {
 void _collect_ysort_children(RenderingServerCanvas::Item *p_canvas_item, Transform2D p_transform, RenderingServerCanvas::Item *p_material_owner, RenderingServerCanvas::Item **r_items, int &r_index) {
@@ -298,28 +298,7 @@ void RenderingServerCanvas::_cull_canvas_item(Item *p_canvas_item, const Transfo
 	}
 	}
 }
 }
 
 
-void RenderingServerCanvas::_light_mask_canvas_items(int p_z, RasterizerCanvas::Item *p_canvas_item, RasterizerCanvas::Light *p_masked_lights) {
-	if (!p_masked_lights) {
-		return;
-	}
-
-	RasterizerCanvas::Item *ci = p_canvas_item;
-
-	while (ci) {
-		RasterizerCanvas::Light *light = p_masked_lights;
-		while (light) {
-			if (ci->light_mask & light->item_mask && p_z >= light->z_min && p_z <= light->z_max && ci->global_rect_cache.intersects_transformed(light->xform_cache, light->rect_cache)) {
-				ci->light_masked = true;
-			}
-
-			light = light->mask_next_ptr;
-		}
-
-		ci = ci->next;
-	}
-}
-
-void RenderingServerCanvas::render_canvas(RID p_render_target, Canvas *p_canvas, const Transform2D &p_transform, RasterizerCanvas::Light *p_lights, RasterizerCanvas::Light *p_masked_lights, const Rect2 &p_clip_rect, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_transforms_to_pixel, bool p_snap_2d_vertices_to_pixel) {
+void RenderingServerCanvas::render_canvas(RID p_render_target, Canvas *p_canvas, const Transform2D &p_transform, RasterizerCanvas::Light *p_lights, RasterizerCanvas::Light *p_directional_lights, const Rect2 &p_clip_rect, RenderingServer::CanvasItemTextureFilter p_default_filter, RenderingServer::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_transforms_to_pixel, bool p_snap_2d_vertices_to_pixel) {
 	RENDER_TIMESTAMP(">Render Canvas");
 	RENDER_TIMESTAMP(">Render Canvas");
 
 
 	snapping_2d_transforms_to_pixel = p_snap_2d_transforms_to_pixel;
 	snapping_2d_transforms_to_pixel = p_snap_2d_transforms_to_pixel;
@@ -341,26 +320,26 @@ void RenderingServerCanvas::render_canvas(RID p_render_target, Canvas *p_canvas,
 	}
 	}
 
 
 	if (!has_mirror) {
 	if (!has_mirror) {
-		_render_canvas_item_tree(p_render_target, ci, l, nullptr, p_transform, p_clip_rect, p_canvas->modulate, p_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel);
+		_render_canvas_item_tree(p_render_target, ci, l, nullptr, p_transform, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel);
 
 
 	} else {
 	} else {
 		//used for parallaxlayer mirroring
 		//used for parallaxlayer mirroring
 		for (int i = 0; i < l; i++) {
 		for (int i = 0; i < l; i++) {
 			const Canvas::ChildItem &ci2 = p_canvas->child_items[i];
 			const Canvas::ChildItem &ci2 = p_canvas->child_items[i];
-			_render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, p_transform, p_clip_rect, p_canvas->modulate, p_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel);
+			_render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, p_transform, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel);
 
 
 			//mirroring (useful for scrolling backgrounds)
 			//mirroring (useful for scrolling backgrounds)
 			if (ci2.mirror.x != 0) {
 			if (ci2.mirror.x != 0) {
 				Transform2D xform2 = p_transform * Transform2D(0, Vector2(ci2.mirror.x, 0));
 				Transform2D xform2 = p_transform * Transform2D(0, Vector2(ci2.mirror.x, 0));
-				_render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, xform2, p_clip_rect, p_canvas->modulate, p_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel);
+				_render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, xform2, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel);
 			}
 			}
 			if (ci2.mirror.y != 0) {
 			if (ci2.mirror.y != 0) {
 				Transform2D xform2 = p_transform * Transform2D(0, Vector2(0, ci2.mirror.y));
 				Transform2D xform2 = p_transform * Transform2D(0, Vector2(0, ci2.mirror.y));
-				_render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, xform2, p_clip_rect, p_canvas->modulate, p_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel);
+				_render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, xform2, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel);
 			}
 			}
 			if (ci2.mirror.y != 0 && ci2.mirror.x != 0) {
 			if (ci2.mirror.y != 0 && ci2.mirror.x != 0) {
 				Transform2D xform2 = p_transform * Transform2D(0, ci2.mirror);
 				Transform2D xform2 = p_transform * Transform2D(0, ci2.mirror);
-				_render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, xform2, p_clip_rect, p_canvas->modulate, p_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel);
+				_render_canvas_item_tree(p_render_target, nullptr, 0, ci2.item, xform2, p_clip_rect, p_canvas->modulate, p_lights, p_directional_lights, p_default_filter, p_default_repeat, p_snap_2d_vertices_to_pixel);
 			}
 			}
 		}
 		}
 	}
 	}
@@ -1040,13 +1019,38 @@ RID RenderingServerCanvas::canvas_light_create() {
 	return canvas_light_owner.make_rid(clight);
 	return canvas_light_owner.make_rid(clight);
 }
 }
 
 
+void RenderingServerCanvas::canvas_light_set_mode(RID p_light, RS::CanvasLightMode p_mode) {
+	RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light);
+	ERR_FAIL_COND(!clight);
+
+	if (clight->mode == p_mode) {
+		return;
+	}
+
+	RID canvas = clight->canvas;
+
+	if (canvas.is_valid()) {
+		canvas_light_attach_to_canvas(p_light, RID());
+	}
+
+	clight->mode = p_mode;
+
+	if (canvas.is_valid()) {
+		canvas_light_attach_to_canvas(p_light, canvas);
+	}
+}
+
 void RenderingServerCanvas::canvas_light_attach_to_canvas(RID p_light, RID p_canvas) {
 void RenderingServerCanvas::canvas_light_attach_to_canvas(RID p_light, RID p_canvas) {
 	RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light);
 	RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light);
 	ERR_FAIL_COND(!clight);
 	ERR_FAIL_COND(!clight);
 
 
 	if (clight->canvas.is_valid()) {
 	if (clight->canvas.is_valid()) {
 		Canvas *canvas = canvas_owner.getornull(clight->canvas);
 		Canvas *canvas = canvas_owner.getornull(clight->canvas);
-		canvas->lights.erase(clight);
+		if (clight->mode == RS::CANVAS_LIGHT_MODE_POINT) {
+			canvas->lights.erase(clight);
+		} else {
+			canvas->directional_lights.erase(clight);
+		}
 	}
 	}
 
 
 	if (!canvas_owner.owns(p_canvas)) {
 	if (!canvas_owner.owns(p_canvas)) {
@@ -1057,7 +1061,11 @@ void RenderingServerCanvas::canvas_light_attach_to_canvas(RID p_light, RID p_can
 
 
 	if (clight->canvas.is_valid()) {
 	if (clight->canvas.is_valid()) {
 		Canvas *canvas = canvas_owner.getornull(clight->canvas);
 		Canvas *canvas = canvas_owner.getornull(clight->canvas);
-		canvas->lights.insert(clight);
+		if (clight->mode == RS::CANVAS_LIGHT_MODE_POINT) {
+			canvas->lights.insert(clight);
+		} else {
+			canvas->directional_lights.insert(clight);
+		}
 	}
 	}
 }
 }
 
 
@@ -1068,7 +1076,7 @@ void RenderingServerCanvas::canvas_light_set_enabled(RID p_light, bool p_enabled
 	clight->enabled = p_enabled;
 	clight->enabled = p_enabled;
 }
 }
 
 
-void RenderingServerCanvas::canvas_light_set_scale(RID p_light, float p_scale) {
+void RenderingServerCanvas::canvas_light_set_texture_scale(RID p_light, float p_scale) {
 	RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light);
 	RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light);
 	ERR_FAIL_COND(!clight);
 	ERR_FAIL_COND(!clight);
 
 
@@ -1152,11 +1160,18 @@ void RenderingServerCanvas::canvas_light_set_item_shadow_cull_mask(RID p_light,
 	clight->item_shadow_mask = p_mask;
 	clight->item_shadow_mask = p_mask;
 }
 }
 
 
-void RenderingServerCanvas::canvas_light_set_mode(RID p_light, RS::CanvasLightMode p_mode) {
+void RenderingServerCanvas::canvas_light_set_directional_distance(RID p_light, float p_distance) {
 	RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light);
 	RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light);
 	ERR_FAIL_COND(!clight);
 	ERR_FAIL_COND(!clight);
 
 
-	clight->mode = p_mode;
+	clight->directional_distance = p_distance;
+}
+
+void RenderingServerCanvas::canvas_light_set_blend_mode(RID p_light, RS::CanvasLightBlendMode p_mode) {
+	RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light);
+	ERR_FAIL_COND(!clight);
+
+	clight->blend_mode = p_mode;
 }
 }
 
 
 void RenderingServerCanvas::canvas_light_set_shadow_enabled(RID p_light, bool p_enabled) {
 void RenderingServerCanvas::canvas_light_set_shadow_enabled(RID p_light, bool p_enabled) {

+ 7 - 5
servers/rendering/rendering_server_canvas.h

@@ -116,6 +116,7 @@ public:
 		};
 		};
 
 
 		Set<RasterizerCanvas::Light *> lights;
 		Set<RasterizerCanvas::Light *> lights;
+		Set<RasterizerCanvas::Light *> directional_lights;
 
 
 		Set<RasterizerCanvas::LightOccluderInstance *> occluders;
 		Set<RasterizerCanvas::LightOccluderInstance *> occluders;
 
 
@@ -155,15 +156,14 @@ public:
 	bool snapping_2d_transforms_to_pixel = false;
 	bool snapping_2d_transforms_to_pixel = false;
 
 
 private:
 private:
-	void _render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RasterizerCanvas::Light *p_lights, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel);
+	void _render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RasterizerCanvas::Light *p_lights, RasterizerCanvas::Light *p_directional_lights, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel);
 	void _cull_canvas_item(Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RasterizerCanvas::Item **z_list, RasterizerCanvas::Item **z_last_list, Item *p_canvas_clip, Item *p_material_owner);
 	void _cull_canvas_item(Item *p_canvas_item, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RasterizerCanvas::Item **z_list, RasterizerCanvas::Item **z_last_list, Item *p_canvas_clip, Item *p_material_owner);
-	void _light_mask_canvas_items(int p_z, RasterizerCanvas::Item *p_canvas_item, RasterizerCanvas::Light *p_masked_lights);
 
 
 	RasterizerCanvas::Item **z_list;
 	RasterizerCanvas::Item **z_list;
 	RasterizerCanvas::Item **z_last_list;
 	RasterizerCanvas::Item **z_last_list;
 
 
 public:
 public:
-	void render_canvas(RID p_render_target, Canvas *p_canvas, const Transform2D &p_transform, RasterizerCanvas::Light *p_lights, RasterizerCanvas::Light *p_masked_lights, const Rect2 &p_clip_rect, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_transforms_to_pixel, bool p_snap_2d_vertices_to_pixel);
+	void render_canvas(RID p_render_target, Canvas *p_canvas, const Transform2D &p_transform, RasterizerCanvas::Light *p_lights, RasterizerCanvas::Light *p_directional_lights, const Rect2 &p_clip_rect, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_transforms_to_pixel, bool p_snap_2d_vertices_to_pixel);
 
 
 	RID canvas_create();
 	RID canvas_create();
 	void canvas_set_item_mirroring(RID p_canvas, RID p_item, const Point2 &p_mirroring);
 	void canvas_set_item_mirroring(RID p_canvas, RID p_item, const Point2 &p_mirroring);
@@ -220,9 +220,10 @@ public:
 	void canvas_item_set_canvas_group_mode(RID p_item, RS::CanvasGroupMode p_mode, float p_clear_margin = 5.0, bool p_fit_empty = false, float p_fit_margin = 0.0, bool p_blur_mipmaps = false);
 	void canvas_item_set_canvas_group_mode(RID p_item, RS::CanvasGroupMode p_mode, float p_clear_margin = 5.0, bool p_fit_empty = false, float p_fit_margin = 0.0, bool p_blur_mipmaps = false);
 
 
 	RID canvas_light_create();
 	RID canvas_light_create();
+	void canvas_light_set_mode(RID p_light, RS::CanvasLightMode p_mode);
 	void canvas_light_attach_to_canvas(RID p_light, RID p_canvas);
 	void canvas_light_attach_to_canvas(RID p_light, RID p_canvas);
 	void canvas_light_set_enabled(RID p_light, bool p_enabled);
 	void canvas_light_set_enabled(RID p_light, bool p_enabled);
-	void canvas_light_set_scale(RID p_light, float p_scale);
+	void canvas_light_set_texture_scale(RID p_light, float p_scale);
 	void canvas_light_set_transform(RID p_light, const Transform2D &p_transform);
 	void canvas_light_set_transform(RID p_light, const Transform2D &p_transform);
 	void canvas_light_set_texture(RID p_light, RID p_texture);
 	void canvas_light_set_texture(RID p_light, RID p_texture);
 	void canvas_light_set_texture_offset(RID p_light, const Vector2 &p_offset);
 	void canvas_light_set_texture_offset(RID p_light, const Vector2 &p_offset);
@@ -233,8 +234,9 @@ public:
 	void canvas_light_set_layer_range(RID p_light, int p_min_layer, int p_max_layer);
 	void canvas_light_set_layer_range(RID p_light, int p_min_layer, int p_max_layer);
 	void canvas_light_set_item_cull_mask(RID p_light, int p_mask);
 	void canvas_light_set_item_cull_mask(RID p_light, int p_mask);
 	void canvas_light_set_item_shadow_cull_mask(RID p_light, int p_mask);
 	void canvas_light_set_item_shadow_cull_mask(RID p_light, int p_mask);
+	void canvas_light_set_directional_distance(RID p_light, float p_distance);
 
 
-	void canvas_light_set_mode(RID p_light, RS::CanvasLightMode p_mode);
+	void canvas_light_set_blend_mode(RID p_light, RS::CanvasLightBlendMode p_mode);
 
 
 	void canvas_light_set_shadow_enabled(RID p_light, bool p_enabled);
 	void canvas_light_set_shadow_enabled(RID p_light, bool p_enabled);
 	void canvas_light_set_shadow_filter(RID p_light, RS::CanvasLightShadowFilter p_filter);
 	void canvas_light_set_shadow_filter(RID p_light, RS::CanvasLightShadowFilter p_filter);

+ 6 - 2
servers/rendering/rendering_server_raster.h

@@ -748,9 +748,12 @@ public:
 	BIND6(canvas_item_set_canvas_group_mode, RID, CanvasGroupMode, float, bool, float, bool)
 	BIND6(canvas_item_set_canvas_group_mode, RID, CanvasGroupMode, float, bool, float, bool)
 
 
 	BIND0R(RID, canvas_light_create)
 	BIND0R(RID, canvas_light_create)
+
+	BIND2(canvas_light_set_mode, RID, CanvasLightMode)
+
 	BIND2(canvas_light_attach_to_canvas, RID, RID)
 	BIND2(canvas_light_attach_to_canvas, RID, RID)
 	BIND2(canvas_light_set_enabled, RID, bool)
 	BIND2(canvas_light_set_enabled, RID, bool)
-	BIND2(canvas_light_set_scale, RID, float)
+	BIND2(canvas_light_set_texture_scale, RID, float)
 	BIND2(canvas_light_set_transform, RID, const Transform2D &)
 	BIND2(canvas_light_set_transform, RID, const Transform2D &)
 	BIND2(canvas_light_set_texture, RID, RID)
 	BIND2(canvas_light_set_texture, RID, RID)
 	BIND2(canvas_light_set_texture_offset, RID, const Vector2 &)
 	BIND2(canvas_light_set_texture_offset, RID, const Vector2 &)
@@ -761,8 +764,9 @@ public:
 	BIND3(canvas_light_set_layer_range, RID, int, int)
 	BIND3(canvas_light_set_layer_range, RID, int, int)
 	BIND2(canvas_light_set_item_cull_mask, RID, int)
 	BIND2(canvas_light_set_item_cull_mask, RID, int)
 	BIND2(canvas_light_set_item_shadow_cull_mask, RID, int)
 	BIND2(canvas_light_set_item_shadow_cull_mask, RID, int)
+	BIND2(canvas_light_set_directional_distance, RID, float)
 
 
-	BIND2(canvas_light_set_mode, RID, CanvasLightMode)
+	BIND2(canvas_light_set_blend_mode, RID, CanvasLightBlendMode)
 
 
 	BIND2(canvas_light_set_shadow_enabled, RID, bool)
 	BIND2(canvas_light_set_shadow_enabled, RID, bool)
 	BIND2(canvas_light_set_shadow_filter, RID, CanvasLightShadowFilter)
 	BIND2(canvas_light_set_shadow_filter, RID, CanvasLightShadowFilter)

+ 120 - 6
servers/rendering/rendering_server_viewport.cpp

@@ -142,11 +142,15 @@ void RenderingServerViewport::_draw_viewport(Viewport *p_viewport, XRInterface::
 		Rect2 clip_rect(0, 0, p_viewport->size.x, p_viewport->size.y);
 		Rect2 clip_rect(0, 0, p_viewport->size.x, p_viewport->size.y);
 		RasterizerCanvas::Light *lights = nullptr;
 		RasterizerCanvas::Light *lights = nullptr;
 		RasterizerCanvas::Light *lights_with_shadow = nullptr;
 		RasterizerCanvas::Light *lights_with_shadow = nullptr;
-		RasterizerCanvas::Light *lights_with_mask = nullptr;
+
+		RasterizerCanvas::Light *directional_lights = nullptr;
+		RasterizerCanvas::Light *directional_lights_with_shadow = nullptr;
+
 		Rect2 shadow_rect;
 		Rect2 shadow_rect;
 
 
 		int light_count = 0;
 		int light_count = 0;
 		int shadow_count = 0;
 		int shadow_count = 0;
+		int directional_light_count = 0;
 
 
 		RENDER_TIMESTAMP("Cull Canvas Lights");
 		RENDER_TIMESTAMP("Cull Canvas Lights");
 		for (Map<RID, Viewport::CanvasData>::Element *E = p_viewport->canvas_map.front(); E; E = E->next()) {
 		for (Map<RID, Viewport::CanvasData>::Element *E = p_viewport->canvas_map.front(); E; E = E->next()) {
@@ -186,10 +190,6 @@ void RenderingServerViewport::_draw_viewport(Viewport *p_viewport, XRInterface::
 							lights_with_shadow = cl;
 							lights_with_shadow = cl;
 							cl->radius_cache = cl->rect_cache.size.length();
 							cl->radius_cache = cl->rect_cache.size.length();
 						}
 						}
-						if (cl->mode == RS::CANVAS_LIGHT_MODE_MASK) {
-							cl->mask_next_ptr = lights_with_mask;
-							lights_with_mask = cl;
-						}
 
 
 						light_count++;
 						light_count++;
 					}
 					}
@@ -199,6 +199,26 @@ void RenderingServerViewport::_draw_viewport(Viewport *p_viewport, XRInterface::
 				}
 				}
 			}
 			}
 
 
+			for (Set<RasterizerCanvas::Light *>::Element *F = canvas->directional_lights.front(); F; F = F->next()) {
+				RasterizerCanvas::Light *cl = F->get();
+				if (cl->enabled) {
+					cl->filter_next_ptr = directional_lights;
+					directional_lights = cl;
+					cl->xform_cache = xf * cl->xform;
+					cl->xform_cache.elements[2] = Vector2(); //translation is pointless
+					if (cl->use_shadow) {
+						cl->shadows_next_ptr = directional_lights_with_shadow;
+						directional_lights_with_shadow = cl;
+					}
+
+					directional_light_count++;
+
+					if (directional_light_count == RS::MAX_2D_DIRECTIONAL_LIGHTS) {
+						break;
+					}
+				}
+			}
+
 			canvas_map[Viewport::CanvasKey(E->key(), E->get().layer, E->get().sublayer)] = &E->get();
 			canvas_map[Viewport::CanvasKey(E->key(), E->get().layer, E->get().sublayer)] = &E->get();
 		}
 		}
 
 
@@ -240,6 +260,90 @@ void RenderingServerViewport::_draw_viewport(Viewport *p_viewport, XRInterface::
 			RENDER_TIMESTAMP("<End rendering 2D Shadows");
 			RENDER_TIMESTAMP("<End rendering 2D Shadows");
 		}
 		}
 
 
+		if (directional_lights_with_shadow) {
+			//update shadows if any
+			RasterizerCanvas::Light *light = directional_lights_with_shadow;
+			while (light) {
+				Vector2 light_dir = -light->xform_cache.elements[1].normalized(); // Y is light direction
+				float cull_distance = light->directional_distance;
+
+				Vector2 light_dir_sign;
+				light_dir_sign.x = (ABS(light_dir.x) < CMP_EPSILON) ? 0.0 : ((light_dir.x > 0.0) ? 1.0 : -1.0);
+				light_dir_sign.y = (ABS(light_dir.y) < CMP_EPSILON) ? 0.0 : ((light_dir.y > 0.0) ? 1.0 : -1.0);
+
+				Vector2 points[6];
+				int point_count = 0;
+
+				for (int j = 0; j < 4; j++) {
+					static const Vector2 signs[4] = { Vector2(1, 1), Vector2(1, 0), Vector2(0, 0), Vector2(0, 1) };
+					Vector2 sign_cmp = signs[j] * 2.0 - Vector2(1.0, 1.0);
+					Vector2 point = clip_rect.position + clip_rect.size * signs[j];
+
+					if (sign_cmp == light_dir_sign) {
+						//both point in same direction, plot offseted
+						points[point_count++] = point + light_dir * cull_distance;
+					} else if (sign_cmp.x == light_dir_sign.x || sign_cmp.y == light_dir_sign.y) {
+						int next_j = (j + 1) % 4;
+						Vector2 next_sign_cmp = signs[next_j] * 2.0 - Vector2(1.0, 1.0);
+
+						//one point in the same direction, plot segment
+
+						if (next_sign_cmp.x == light_dir_sign.x || next_sign_cmp.y == light_dir_sign.y) {
+							if (light_dir_sign.x != 0.0 || light_dir_sign.y != 0.0) {
+								points[point_count++] = point;
+							}
+							points[point_count++] = point + light_dir * cull_distance;
+						} else {
+							points[point_count++] = point + light_dir * cull_distance;
+							if (light_dir_sign.x != 0.0 || light_dir_sign.y != 0.0) {
+								points[point_count++] = point;
+							}
+						}
+					} else {
+						//plot normally
+						points[point_count++] = point;
+					}
+				}
+
+				Vector2 xf_points[6];
+
+				RasterizerCanvas::LightOccluderInstance *occluders = nullptr;
+
+				RENDER_TIMESTAMP(">Render Directional 2D Shadows");
+
+				//make list of occluders
+				int occ_cullded = 0;
+				for (Map<RID, Viewport::CanvasData>::Element *E = p_viewport->canvas_map.front(); E; E = E->next()) {
+					RenderingServerCanvas::Canvas *canvas = static_cast<RenderingServerCanvas::Canvas *>(E->get().canvas);
+					Transform2D xf = _canvas_get_transform(p_viewport, canvas, &E->get(), clip_rect.size);
+
+					for (Set<RasterizerCanvas::LightOccluderInstance *>::Element *F = canvas->occluders.front(); F; F = F->next()) {
+						if (!F->get()->enabled) {
+							continue;
+						}
+						F->get()->xform_cache = xf * F->get()->xform;
+						Transform2D localizer = F->get()->xform_cache.affine_inverse();
+
+						for (int j = 0; j < point_count; j++) {
+							xf_points[j] = localizer.xform(points[j]);
+						}
+						if (F->get()->aabb_cache.intersects_filled_polygon(xf_points, point_count)) {
+							F->get()->next = occluders;
+							occluders = F->get();
+							occ_cullded++;
+						}
+					}
+				}
+
+				RSG::canvas_render->light_update_directional_shadow(light->light_internal, shadow_count++, light->xform_cache, light->item_shadow_mask, cull_distance, clip_rect, occluders);
+
+				light = light->shadows_next_ptr;
+			}
+
+			//RSG::canvas_render->reset_canvas();
+			RENDER_TIMESTAMP("<Render Directional 2D Shadows");
+		}
+
 		if (scenario_draw_canvas_bg && canvas_map.front() && canvas_map.front()->key().get_layer() > scenario_canvas_max_layer) {
 		if (scenario_draw_canvas_bg && canvas_map.front() && canvas_map.front()->key().get_layer() > scenario_canvas_max_layer) {
 			if (!can_draw_3d) {
 			if (!can_draw_3d) {
 				RSG::scene->render_empty_scene(p_viewport->render_buffers, p_viewport->scenario, p_viewport->shadow_atlas);
 				RSG::scene->render_empty_scene(p_viewport->render_buffers, p_viewport->scenario, p_viewport->shadow_atlas);
@@ -255,6 +359,7 @@ void RenderingServerViewport::_draw_viewport(Viewport *p_viewport, XRInterface::
 			Transform2D xform = _canvas_get_transform(p_viewport, canvas, E->get(), clip_rect.size);
 			Transform2D xform = _canvas_get_transform(p_viewport, canvas, E->get(), clip_rect.size);
 
 
 			RasterizerCanvas::Light *canvas_lights = nullptr;
 			RasterizerCanvas::Light *canvas_lights = nullptr;
+			RasterizerCanvas::Light *canvas_directional_lights = nullptr;
 
 
 			RasterizerCanvas::Light *ptr = lights;
 			RasterizerCanvas::Light *ptr = lights;
 			while (ptr) {
 			while (ptr) {
@@ -265,7 +370,16 @@ void RenderingServerViewport::_draw_viewport(Viewport *p_viewport, XRInterface::
 				ptr = ptr->filter_next_ptr;
 				ptr = ptr->filter_next_ptr;
 			}
 			}
 
 
-			RSG::canvas->render_canvas(p_viewport->render_target, canvas, xform, canvas_lights, lights_with_mask, clip_rect, p_viewport->texture_filter, p_viewport->texture_repeat, p_viewport->snap_2d_transforms_to_pixel, p_viewport->snap_2d_vertices_to_pixel);
+			ptr = directional_lights;
+			while (ptr) {
+				if (E->get()->layer >= ptr->layer_min && E->get()->layer <= ptr->layer_max) {
+					ptr->next_ptr = canvas_directional_lights;
+					canvas_directional_lights = ptr;
+				}
+				ptr = ptr->filter_next_ptr;
+			}
+
+			RSG::canvas->render_canvas(p_viewport->render_target, canvas, xform, canvas_lights, canvas_directional_lights, clip_rect, p_viewport->texture_filter, p_viewport->texture_repeat, p_viewport->snap_2d_transforms_to_pixel, p_viewport->snap_2d_vertices_to_pixel);
 			i++;
 			i++;
 
 
 			if (scenario_draw_canvas_bg && E->key().get_layer() >= scenario_canvas_max_layer) {
 			if (scenario_draw_canvas_bg && E->key().get_layer() >= scenario_canvas_max_layer) {

+ 6 - 2
servers/rendering/rendering_server_wrap_mt.h

@@ -647,9 +647,12 @@ public:
 	FUNC6(canvas_item_set_canvas_group_mode, RID, CanvasGroupMode, float, bool, float, bool)
 	FUNC6(canvas_item_set_canvas_group_mode, RID, CanvasGroupMode, float, bool, float, bool)
 
 
 	FUNC0R(RID, canvas_light_create)
 	FUNC0R(RID, canvas_light_create)
+
+	FUNC2(canvas_light_set_mode, RID, CanvasLightMode)
+
 	FUNC2(canvas_light_attach_to_canvas, RID, RID)
 	FUNC2(canvas_light_attach_to_canvas, RID, RID)
 	FUNC2(canvas_light_set_enabled, RID, bool)
 	FUNC2(canvas_light_set_enabled, RID, bool)
-	FUNC2(canvas_light_set_scale, RID, float)
+	FUNC2(canvas_light_set_texture_scale, RID, float)
 	FUNC2(canvas_light_set_transform, RID, const Transform2D &)
 	FUNC2(canvas_light_set_transform, RID, const Transform2D &)
 	FUNC2(canvas_light_set_texture, RID, RID)
 	FUNC2(canvas_light_set_texture, RID, RID)
 	FUNC2(canvas_light_set_texture_offset, RID, const Vector2 &)
 	FUNC2(canvas_light_set_texture_offset, RID, const Vector2 &)
@@ -660,8 +663,9 @@ public:
 	FUNC3(canvas_light_set_layer_range, RID, int, int)
 	FUNC3(canvas_light_set_layer_range, RID, int, int)
 	FUNC2(canvas_light_set_item_cull_mask, RID, int)
 	FUNC2(canvas_light_set_item_cull_mask, RID, int)
 	FUNC2(canvas_light_set_item_shadow_cull_mask, RID, int)
 	FUNC2(canvas_light_set_item_shadow_cull_mask, RID, int)
+	FUNC2(canvas_light_set_directional_distance, RID, float)
 
 
-	FUNC2(canvas_light_set_mode, RID, CanvasLightMode)
+	FUNC2(canvas_light_set_blend_mode, RID, CanvasLightBlendMode)
 
 
 	FUNC2(canvas_light_set_shadow_enabled, RID, bool)
 	FUNC2(canvas_light_set_shadow_enabled, RID, bool)
 	FUNC2(canvas_light_set_shadow_filter, RID, CanvasLightShadowFilter)
 	FUNC2(canvas_light_set_shadow_filter, RID, CanvasLightShadowFilter)

+ 4 - 5
servers/rendering_server.cpp

@@ -1833,7 +1833,7 @@ void RenderingServer::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("canvas_light_create"), &RenderingServer::canvas_light_create);
 	ClassDB::bind_method(D_METHOD("canvas_light_create"), &RenderingServer::canvas_light_create);
 	ClassDB::bind_method(D_METHOD("canvas_light_attach_to_canvas", "light", "canvas"), &RenderingServer::canvas_light_attach_to_canvas);
 	ClassDB::bind_method(D_METHOD("canvas_light_attach_to_canvas", "light", "canvas"), &RenderingServer::canvas_light_attach_to_canvas);
 	ClassDB::bind_method(D_METHOD("canvas_light_set_enabled", "light", "enabled"), &RenderingServer::canvas_light_set_enabled);
 	ClassDB::bind_method(D_METHOD("canvas_light_set_enabled", "light", "enabled"), &RenderingServer::canvas_light_set_enabled);
-	ClassDB::bind_method(D_METHOD("canvas_light_set_scale", "light", "scale"), &RenderingServer::canvas_light_set_scale);
+	ClassDB::bind_method(D_METHOD("canvas_light_set_texture_scale", "light", "scale"), &RenderingServer::canvas_light_set_texture_scale);
 	ClassDB::bind_method(D_METHOD("canvas_light_set_transform", "light", "transform"), &RenderingServer::canvas_light_set_transform);
 	ClassDB::bind_method(D_METHOD("canvas_light_set_transform", "light", "transform"), &RenderingServer::canvas_light_set_transform);
 	ClassDB::bind_method(D_METHOD("canvas_light_set_texture", "light", "texture"), &RenderingServer::canvas_light_set_texture);
 	ClassDB::bind_method(D_METHOD("canvas_light_set_texture", "light", "texture"), &RenderingServer::canvas_light_set_texture);
 	ClassDB::bind_method(D_METHOD("canvas_light_set_texture_offset", "light", "offset"), &RenderingServer::canvas_light_set_texture_offset);
 	ClassDB::bind_method(D_METHOD("canvas_light_set_texture_offset", "light", "offset"), &RenderingServer::canvas_light_set_texture_offset);
@@ -2190,10 +2190,9 @@ void RenderingServer::_bind_methods() {
 	BIND_ENUM_CONSTANT(CANVAS_ITEM_TEXTURE_REPEAT_MIRROR);
 	BIND_ENUM_CONSTANT(CANVAS_ITEM_TEXTURE_REPEAT_MIRROR);
 	BIND_ENUM_CONSTANT(CANVAS_ITEM_TEXTURE_REPEAT_MAX);
 	BIND_ENUM_CONSTANT(CANVAS_ITEM_TEXTURE_REPEAT_MAX);
 
 
-	BIND_ENUM_CONSTANT(CANVAS_LIGHT_MODE_ADD);
-	BIND_ENUM_CONSTANT(CANVAS_LIGHT_MODE_SUB);
-	BIND_ENUM_CONSTANT(CANVAS_LIGHT_MODE_MIX);
-	BIND_ENUM_CONSTANT(CANVAS_LIGHT_MODE_MASK);
+	BIND_ENUM_CONSTANT(CANVAS_LIGHT_BLEND_MODE_ADD);
+	BIND_ENUM_CONSTANT(CANVAS_LIGHT_BLEND_MODE_SUB);
+	BIND_ENUM_CONSTANT(CANVAS_LIGHT_BLEND_MODE_MIX);
 
 
 	BIND_ENUM_CONSTANT(CANVAS_LIGHT_FILTER_NONE);
 	BIND_ENUM_CONSTANT(CANVAS_LIGHT_FILTER_NONE);
 	BIND_ENUM_CONSTANT(CANVAS_LIGHT_FILTER_PCF5);
 	BIND_ENUM_CONSTANT(CANVAS_LIGHT_FILTER_PCF5);

+ 21 - 9
servers/rendering_server.h

@@ -77,6 +77,7 @@ public:
 		CANVAS_ITEM_Z_MAX = 4096,
 		CANVAS_ITEM_Z_MAX = 4096,
 		MAX_GLOW_LEVELS = 7,
 		MAX_GLOW_LEVELS = 7,
 		MAX_CURSORS = 8,
 		MAX_CURSORS = 8,
+		MAX_2D_DIRECTIONAL_LIGHTS = 8
 	};
 	};
 
 
 	/* TEXTURE API */
 	/* TEXTURE API */
@@ -1195,12 +1196,17 @@ public:
 	virtual void canvas_item_set_canvas_group_mode(RID p_item, CanvasGroupMode p_mode, float p_clear_margin = 5.0, bool p_fit_empty = false, float p_fit_margin = 0.0, bool p_blur_mipmaps = false) = 0;
 	virtual void canvas_item_set_canvas_group_mode(RID p_item, CanvasGroupMode p_mode, float p_clear_margin = 5.0, bool p_fit_empty = false, float p_fit_margin = 0.0, bool p_blur_mipmaps = false) = 0;
 
 
 	virtual RID canvas_light_create() = 0;
 	virtual RID canvas_light_create() = 0;
+
+	enum CanvasLightMode {
+		CANVAS_LIGHT_MODE_POINT,
+		CANVAS_LIGHT_MODE_DIRECTIONAL,
+	};
+
+	virtual void canvas_light_set_mode(RID p_light, CanvasLightMode p_mode) = 0;
+
 	virtual void canvas_light_attach_to_canvas(RID p_light, RID p_canvas) = 0;
 	virtual void canvas_light_attach_to_canvas(RID p_light, RID p_canvas) = 0;
 	virtual void canvas_light_set_enabled(RID p_light, bool p_enabled) = 0;
 	virtual void canvas_light_set_enabled(RID p_light, bool p_enabled) = 0;
-	virtual void canvas_light_set_scale(RID p_light, float p_scale) = 0;
 	virtual void canvas_light_set_transform(RID p_light, const Transform2D &p_transform) = 0;
 	virtual void canvas_light_set_transform(RID p_light, const Transform2D &p_transform) = 0;
-	virtual void canvas_light_set_texture(RID p_light, RID p_texture) = 0;
-	virtual void canvas_light_set_texture_offset(RID p_light, const Vector2 &p_offset) = 0;
 	virtual void canvas_light_set_color(RID p_light, const Color &p_color) = 0;
 	virtual void canvas_light_set_color(RID p_light, const Color &p_color) = 0;
 	virtual void canvas_light_set_height(RID p_light, float p_height) = 0;
 	virtual void canvas_light_set_height(RID p_light, float p_height) = 0;
 	virtual void canvas_light_set_energy(RID p_light, float p_energy) = 0;
 	virtual void canvas_light_set_energy(RID p_light, float p_energy) = 0;
@@ -1209,14 +1215,19 @@ public:
 	virtual void canvas_light_set_item_cull_mask(RID p_light, int p_mask) = 0;
 	virtual void canvas_light_set_item_cull_mask(RID p_light, int p_mask) = 0;
 	virtual void canvas_light_set_item_shadow_cull_mask(RID p_light, int p_mask) = 0;
 	virtual void canvas_light_set_item_shadow_cull_mask(RID p_light, int p_mask) = 0;
 
 
-	enum CanvasLightMode {
-		CANVAS_LIGHT_MODE_ADD,
-		CANVAS_LIGHT_MODE_SUB,
-		CANVAS_LIGHT_MODE_MIX,
-		CANVAS_LIGHT_MODE_MASK,
+	virtual void canvas_light_set_directional_distance(RID p_light, float p_distance) = 0;
+
+	virtual void canvas_light_set_texture_scale(RID p_light, float p_scale) = 0;
+	virtual void canvas_light_set_texture(RID p_light, RID p_texture) = 0;
+	virtual void canvas_light_set_texture_offset(RID p_light, const Vector2 &p_offset) = 0;
+
+	enum CanvasLightBlendMode {
+		CANVAS_LIGHT_BLEND_MODE_ADD,
+		CANVAS_LIGHT_BLEND_MODE_SUB,
+		CANVAS_LIGHT_BLEND_MODE_MIX,
 	};
 	};
 
 
-	virtual void canvas_light_set_mode(RID p_light, CanvasLightMode p_mode) = 0;
+	virtual void canvas_light_set_blend_mode(RID p_light, CanvasLightBlendMode p_mode) = 0;
 
 
 	enum CanvasLightShadowFilter {
 	enum CanvasLightShadowFilter {
 		CANVAS_LIGHT_FILTER_NONE,
 		CANVAS_LIGHT_FILTER_NONE,
@@ -1435,6 +1446,7 @@ VARIANT_ENUM_CAST(RenderingServer::NinePatchAxisMode);
 VARIANT_ENUM_CAST(RenderingServer::CanvasItemTextureFilter);
 VARIANT_ENUM_CAST(RenderingServer::CanvasItemTextureFilter);
 VARIANT_ENUM_CAST(RenderingServer::CanvasItemTextureRepeat);
 VARIANT_ENUM_CAST(RenderingServer::CanvasItemTextureRepeat);
 VARIANT_ENUM_CAST(RenderingServer::CanvasLightMode);
 VARIANT_ENUM_CAST(RenderingServer::CanvasLightMode);
+VARIANT_ENUM_CAST(RenderingServer::CanvasLightBlendMode);
 VARIANT_ENUM_CAST(RenderingServer::CanvasLightShadowFilter);
 VARIANT_ENUM_CAST(RenderingServer::CanvasLightShadowFilter);
 VARIANT_ENUM_CAST(RenderingServer::CanvasOccluderPolygonCullMode);
 VARIANT_ENUM_CAST(RenderingServer::CanvasOccluderPolygonCullMode);
 VARIANT_ENUM_CAST(RenderingServer::GlobalVariableType);
 VARIANT_ENUM_CAST(RenderingServer::GlobalVariableType);