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.
This commit is contained in:
reduz 2020-11-03 16:51:53 -03:00
parent 3ec10bb07c
commit f123981a96
16 changed files with 869 additions and 364 deletions

View file

@ -244,6 +244,68 @@ struct Rect2 {
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); }
Rect2() {}

View file

@ -33,54 +33,6 @@
#include "core/engine.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() {
if (!is_inside_tree()) {
return;
@ -123,32 +75,6 @@ bool Light2D::is_editor_only() const {
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) {
color = p_color;
RS::get_singleton()->canvas_light_set_color(canvas_light, color);
@ -176,20 +102,6 @@ float Light2D::get_energy() const {
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) {
z_min = p_min_z;
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;
}
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) {
shadow = p_enabled;
RS::get_singleton()->canvas_light_set_shadow_enabled(canvas_light, shadow);
@ -281,6 +184,15 @@ Color Light2D::get_shadow_color() const {
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) {
if (p_what == NOTIFICATION_ENTER_TREE) {
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) {
shadow_smooth = p_amount;
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("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("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("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("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("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("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("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, "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::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_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_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");
@ -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::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_PCF5);
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() {
@ -420,22 +305,170 @@ Light2D::Light2D() {
shadow = false;
color = Color(1, 1, 1);
height = 0;
_scale = 1.0;
z_min = -1024;
z_max = 1024;
layer_min = 0;
layer_max = 0;
item_mask = 1;
item_shadow_mask = 1;
mode = MODE_ADD;
energy = 1.0;
shadow_color = Color(0, 0, 0, 0);
shadow_filter = SHADOW_FILTER_NONE;
shadow_smooth = 0;
blend_mode = BLEND_MODE_ADD;
set_notify_transform(true);
}
Light2D::~Light2D() {
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);
}

View file

@ -37,13 +37,6 @@ class Light2D : public Node2D {
GDCLASS(Light2D, Node2D);
public:
enum Mode {
MODE_ADD,
MODE_SUB,
MODE_MIX,
MODE_MASK,
};
enum ShadowFilter {
SHADOW_FILTER_NONE,
SHADOW_FILTER_PCF5,
@ -51,6 +44,12 @@ public:
SHADOW_FILTER_MAX
};
enum BlendMode {
BLEND_MODE_ADD,
BLEND_MODE_SUB,
BLEND_MODE_MIX,
};
private:
RID canvas_light;
bool enabled;
@ -68,43 +67,25 @@ private:
int item_mask;
int item_shadow_mask;
float shadow_smooth;
Mode mode;
Ref<Texture2D> texture;
Vector2 texture_offset;
ShadowFilter shadow_filter;
BlendMode blend_mode;
void _update_light_visibility();
protected:
_FORCE_INLINE_ RID _get_light() const { return canvas_light; }
void _notification(int p_what);
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_enabled(bool p_enabled);
bool is_enabled() const;
void set_editor_only(bool p_editor_only);
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);
Color get_color() const;
@ -114,9 +95,6 @@ public:
void set_energy(float p_energy);
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);
int get_z_range_min() const;
@ -135,9 +113,6 @@ public:
void set_item_shadow_cull_mask(int p_mask);
int get_item_shadow_cull_mask() const;
void set_mode(Mode p_mode);
Mode get_mode() const;
void set_shadow_enabled(bool p_enabled);
bool is_shadow_enabled() const;
@ -150,13 +125,69 @@ public:
void set_shadow_smooth(float p_amount);
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();
};
VARIANT_ENUM_CAST(Light2D::Mode);
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

View file

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

View file

@ -832,7 +832,9 @@ public:
int layer_max;
int item_mask;
int item_shadow_mask;
float directional_distance;
RS::CanvasLightMode mode;
RS::CanvasLightBlendMode blend_mode;
RID texture;
Vector2 texture_offset;
RID canvas;
@ -854,7 +856,7 @@ public:
Light *shadows_next_ptr;
Light *filter_next_ptr;
Light *next_ptr;
Light *mask_next_ptr;
Light *directional_next_ptr;
RID light_internal;
uint64_t version;
@ -875,16 +877,18 @@ public:
scale = 1.0;
energy = 1.0;
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;
next_ptr = nullptr;
mask_next_ptr = nullptr;
directional_next_ptr = nullptr;
filter_next_ptr = nullptr;
use_shadow = false;
shadow_buffer_size = 2048;
shadow_filter = RS::CANVAS_LIGHT_FILTER_NONE;
shadow_smooth = 0.0;
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;
struct LightOccluderInstance {
@ -1350,6 +1354,7 @@ public:
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_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 void occluder_polygon_set_shape_as_lines(RID p_occluder, const Vector<Vector2> &p_lines) = 0;

View file

@ -416,10 +416,6 @@ void RasterizerCanvasRD::_render_item(RD::DrawListID p_draw_list, const Item *p_
light_count++;
if (light->mode == RS::CANVAS_LIGHT_MODE_MASK) {
base_flags |= FLAGS_USING_LIGHT_MASK;
}
if (light_count == MAX_LIGHTS_PER_ITEM) {
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;
}
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;
@ -1194,51 +1190,83 @@ void RasterizerCanvasRD::_render_items(RID p_to_render_target, int p_item_count,
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;
//setup canvas state uniforms if needed
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
{
Light *l = p_light_list;
uint32_t index = 0;
uint32_t index = light_count;
while (l) {
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].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;
if (clight->shadow.enabled) {
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;
}
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
@ -1439,10 +1504,7 @@ void RasterizerCanvasRD::light_set_use_shadow(RID p_rid, bool 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()) {
//ah, we lack the shadow texture..
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);
}
}
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.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() {
OccluderPolygon occluder;
occluder.point_count = 0;

View file

@ -75,7 +75,6 @@ class RasterizerCanvasRD : public RasterizerCanvas {
FLAGS_CLIP_RECT_UV = (1 << 9),
FLAGS_TRANSPOSE_RECT = (1 << 10),
FLAGS_USING_LIGHT_MASK = (1 << 11),
FLAGS_NINEPACH_DRAW_CENTER = (1 << 12),
FLAGS_USING_PARTICLES = (1 << 13),
@ -269,6 +268,7 @@ class RasterizerCanvasRD : public RasterizerCanvas {
bool enabled = false;
float z_far;
float y_offset;
Transform2D directional_xform;
} shadow;
};
@ -331,12 +331,13 @@ class RasterizerCanvasRD : public RasterizerCanvas {
float screen_transform[16];
float canvas_normal_transform[16];
float canvas_modulate[4];
float screen_pixel_size[2];
float time;
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;
@ -355,6 +356,7 @@ class RasterizerCanvasRD : public RasterizerCanvas {
uint32_t max_lights_per_item;
double time;
} state;
struct PushConstant {
@ -388,6 +390,7 @@ class RasterizerCanvasRD : public RasterizerCanvas {
Item *items[MAX_RENDER_ITEMS];
bool using_directional_lights = false;
RID default_canvas_texture;
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_to_mat4(const Transform &p_transform, float *p_mat4);
void _update_shadow_atlas();
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>());
void free_polygon(PolygonID p_polygon);
@ -416,12 +421,13 @@ public:
void light_set_texture(RID p_rid, RID p_texture);
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_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();
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 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) {}

View file

@ -249,7 +249,7 @@ vec4 light_compute(
inout vec4 shadow_modulate,
vec2 screen_uv,
vec2 uv,
vec4 color) {
vec4 color, bool is_directional) {
vec4 light = vec4(0.0);
/* clang-format off */
LIGHT_SHADER_CODE
@ -302,6 +302,99 @@ float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, flo
#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() {
vec4 color = color_interp;
vec2 uv = uv_interp;
@ -332,6 +425,7 @@ void main() {
color *= texture(sampler2D(color_texture, texture_sampler), uv);
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;
@ -341,7 +435,7 @@ void main() {
bool normal_used = false;
#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.z = sqrt(1.0 - dot(normal.xy, normal.xy));
normal_used = true;
@ -358,7 +452,7 @@ void main() {
bool specular_shininess_used = false;
#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 *= unpackUnorm4x8(draw_data.specular_shininess);
specular_shininess_used = true;
@ -401,13 +495,52 @@ FRAGMENT_SHADER_CODE
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)) {
color = vec4(0.0); //invisible by default due to using light mask
}
color *= canvas_data.canvas_modulation;
#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++) {
if (i >= light_count) {
break;
@ -440,7 +573,7 @@ FRAGMENT_SHADER_CODE
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 = 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
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);
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
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;
//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);
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
shadow_color *= shadow_modulate;
,
shadow_modulate
#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

View file

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

View file

@ -37,7 +37,7 @@
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");
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");
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) {
@ -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");
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) {
_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 {
//used for parallaxlayer mirroring
for (int i = 0; i < l; 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)
if (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) {
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) {
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);
}
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) {
RasterizerCanvas::Light *clight = canvas_light_owner.getornull(p_light);
ERR_FAIL_COND(!clight);
if (clight->canvas.is_valid()) {
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)) {
@ -1057,7 +1061,11 @@ void RenderingServerCanvas::canvas_light_attach_to_canvas(RID p_light, RID p_can
if (clight->canvas.is_valid()) {
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;
}
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);
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;
}
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);
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) {

View file

@ -116,6 +116,7 @@ public:
};
Set<RasterizerCanvas::Light *> lights;
Set<RasterizerCanvas::Light *> directional_lights;
Set<RasterizerCanvas::LightOccluderInstance *> occluders;
@ -155,15 +156,14 @@ public:
bool snapping_2d_transforms_to_pixel = false;
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 _light_mask_canvas_items(int p_z, RasterizerCanvas::Item *p_canvas_item, RasterizerCanvas::Light *p_masked_lights);
RasterizerCanvas::Item **z_list;
RasterizerCanvas::Item **z_last_list;
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();
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);
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_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_texture(RID p_light, RID p_texture);
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_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_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_filter(RID p_light, RS::CanvasLightShadowFilter p_filter);

View file

@ -748,9 +748,12 @@ public:
BIND6(canvas_item_set_canvas_group_mode, RID, CanvasGroupMode, float, bool, float, bool)
BIND0R(RID, canvas_light_create)
BIND2(canvas_light_set_mode, RID, CanvasLightMode)
BIND2(canvas_light_attach_to_canvas, RID, RID)
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_texture, RID, RID)
BIND2(canvas_light_set_texture_offset, RID, const Vector2 &)
@ -761,8 +764,9 @@ public:
BIND3(canvas_light_set_layer_range, RID, int, int)
BIND2(canvas_light_set_item_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_filter, RID, CanvasLightShadowFilter)

View file

@ -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);
RasterizerCanvas::Light *lights = 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;
int light_count = 0;
int shadow_count = 0;
int directional_light_count = 0;
RENDER_TIMESTAMP("Cull Canvas Lights");
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;
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++;
}
@ -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();
}
@ -240,6 +260,90 @@ void RenderingServerViewport::_draw_viewport(Viewport *p_viewport, XRInterface::
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 (!can_draw_3d) {
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);
RasterizerCanvas::Light *canvas_lights = nullptr;
RasterizerCanvas::Light *canvas_directional_lights = nullptr;
RasterizerCanvas::Light *ptr = lights;
while (ptr) {
@ -265,7 +370,16 @@ void RenderingServerViewport::_draw_viewport(Viewport *p_viewport, XRInterface::
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++;
if (scenario_draw_canvas_bg && E->key().get_layer() >= scenario_canvas_max_layer) {

View file

@ -647,9 +647,12 @@ public:
FUNC6(canvas_item_set_canvas_group_mode, RID, CanvasGroupMode, float, bool, float, bool)
FUNC0R(RID, canvas_light_create)
FUNC2(canvas_light_set_mode, RID, CanvasLightMode)
FUNC2(canvas_light_attach_to_canvas, RID, RID)
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_texture, RID, RID)
FUNC2(canvas_light_set_texture_offset, RID, const Vector2 &)
@ -660,8 +663,9 @@ public:
FUNC3(canvas_light_set_layer_range, RID, int, int)
FUNC2(canvas_light_set_item_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_filter, RID, CanvasLightShadowFilter)

View file

@ -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_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_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_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);
@ -2190,10 +2190,9 @@ void RenderingServer::_bind_methods() {
BIND_ENUM_CONSTANT(CANVAS_ITEM_TEXTURE_REPEAT_MIRROR);
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_PCF5);

View file

@ -77,6 +77,7 @@ public:
CANVAS_ITEM_Z_MAX = 4096,
MAX_GLOW_LEVELS = 7,
MAX_CURSORS = 8,
MAX_2D_DIRECTIONAL_LIGHTS = 8
};
/* 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 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_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_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_height(RID p_light, float p_height) = 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_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 {
CANVAS_LIGHT_FILTER_NONE,
@ -1435,6 +1446,7 @@ VARIANT_ENUM_CAST(RenderingServer::NinePatchAxisMode);
VARIANT_ENUM_CAST(RenderingServer::CanvasItemTextureFilter);
VARIANT_ENUM_CAST(RenderingServer::CanvasItemTextureRepeat);
VARIANT_ENUM_CAST(RenderingServer::CanvasLightMode);
VARIANT_ENUM_CAST(RenderingServer::CanvasLightBlendMode);
VARIANT_ENUM_CAST(RenderingServer::CanvasLightShadowFilter);
VARIANT_ENUM_CAST(RenderingServer::CanvasOccluderPolygonCullMode);
VARIANT_ENUM_CAST(RenderingServer::GlobalVariableType);