Merge pull request #28565 from CedNaru/CurvedWidthLine2D

Added a Width Curve to Line2D + UVs fix
This commit is contained in:
Rémi Verschelde 2019-07-01 09:38:54 +02:00 committed by GitHub
commit e4a50999c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 153 additions and 76 deletions

View File

@ -99,6 +99,9 @@
<member name="width" type="float" setter="set_width" getter="get_width" default="10.0">
The line's width.
</member>
<member name="width_curve" type="Curve" setter="set_curve" getter="get_curve">
The line's width varies with the curve. The original width is simply multiply by the value of the Curve.
</member>
</members>
<constants>
<constant name="LINE_JOINT_SHARP" value="0" enum="LineJointMode">

View File

@ -84,10 +84,10 @@ void Line2D::set_points(const PoolVector<Vector2> &p_points) {
update();
}
void Line2D::set_width(float width) {
if (width < 0.0)
width = 0.0;
_width = width;
void Line2D::set_width(float p_width) {
if (p_width < 0.0)
p_width = 0.0;
_width = p_width;
update();
}
@ -95,12 +95,32 @@ float Line2D::get_width() const {
return _width;
}
void Line2D::set_curve(const Ref<Curve> &p_curve) {
// Cleanup previous connection if any
if (_curve.is_valid()) {
_curve->disconnect(CoreStringNames::get_singleton()->changed, this, "_curve_changed");
}
_curve = p_curve;
// Connect to the curve so the line will update when it is changed
if (_curve.is_valid()) {
_curve->connect(CoreStringNames::get_singleton()->changed, this, "_curve_changed");
}
update();
}
Ref<Curve> Line2D::get_curve() const {
return _curve;
}
PoolVector<Vector2> Line2D::get_points() const {
return _points;
}
void Line2D::set_point_position(int i, Vector2 pos) {
_points.set(i, pos);
void Line2D::set_point_position(int i, Vector2 p_pos) {
_points.set(i, p_pos);
update();
}
@ -121,11 +141,11 @@ void Line2D::clear_points() {
}
}
void Line2D::add_point(Vector2 pos, int atpos) {
if (atpos < 0 || _points.size() < atpos) {
_points.append(pos);
void Line2D::add_point(Vector2 p_pos, int p_atpos) {
if (p_atpos < 0 || _points.size() < p_atpos) {
_points.append(p_pos);
} else {
_points.insert(atpos, pos);
_points.insert(p_atpos, p_pos);
}
update();
}
@ -135,8 +155,8 @@ void Line2D::remove_point(int i) {
update();
}
void Line2D::set_default_color(Color color) {
_default_color = color;
void Line2D::set_default_color(Color p_color) {
_default_color = p_color;
update();
}
@ -144,18 +164,18 @@ Color Line2D::get_default_color() const {
return _default_color;
}
void Line2D::set_gradient(const Ref<Gradient> &gradient) {
void Line2D::set_gradient(const Ref<Gradient> &p_gradient) {
// Cleanup previous connection if any
if (_gradient.is_valid()) {
(**_gradient).disconnect(CoreStringNames::get_singleton()->changed, this, "_gradient_changed");
_gradient->disconnect(CoreStringNames::get_singleton()->changed, this, "_gradient_changed");
}
_gradient = gradient;
_gradient = p_gradient;
// Connect to the gradient so the line will update when the ColorRamp is changed
if (_gradient.is_valid()) {
(**_gradient).connect(CoreStringNames::get_singleton()->changed, this, "_gradient_changed");
_gradient->connect(CoreStringNames::get_singleton()->changed, this, "_gradient_changed");
}
update();
@ -165,8 +185,8 @@ Ref<Gradient> Line2D::get_gradient() const {
return _gradient;
}
void Line2D::set_texture(const Ref<Texture> &texture) {
_texture = texture;
void Line2D::set_texture(const Ref<Texture> &p_texture) {
_texture = p_texture;
update();
}
@ -174,8 +194,8 @@ Ref<Texture> Line2D::get_texture() const {
return _texture;
}
void Line2D::set_texture_mode(const LineTextureMode mode) {
_texture_mode = mode;
void Line2D::set_texture_mode(const LineTextureMode p_mode) {
_texture_mode = p_mode;
update();
}
@ -183,8 +203,8 @@ Line2D::LineTextureMode Line2D::get_texture_mode() const {
return _texture_mode;
}
void Line2D::set_joint_mode(LineJointMode mode) {
_joint_mode = mode;
void Line2D::set_joint_mode(LineJointMode p_mode) {
_joint_mode = p_mode;
update();
}
@ -192,8 +212,8 @@ Line2D::LineJointMode Line2D::get_joint_mode() const {
return _joint_mode;
}
void Line2D::set_begin_cap_mode(LineCapMode mode) {
_begin_cap_mode = mode;
void Line2D::set_begin_cap_mode(LineCapMode p_mode) {
_begin_cap_mode = p_mode;
update();
}
@ -201,8 +221,8 @@ Line2D::LineCapMode Line2D::get_begin_cap_mode() const {
return _begin_cap_mode;
}
void Line2D::set_end_cap_mode(LineCapMode mode) {
_end_cap_mode = mode;
void Line2D::set_end_cap_mode(LineCapMode p_mode) {
_end_cap_mode = p_mode;
update();
}
@ -218,10 +238,10 @@ void Line2D::_notification(int p_what) {
}
}
void Line2D::set_sharp_limit(float limit) {
if (limit < 0.f)
limit = 0.f;
_sharp_limit = limit;
void Line2D::set_sharp_limit(float p_limit) {
if (p_limit < 0.f)
p_limit = 0.f;
_sharp_limit = p_limit;
update();
}
@ -229,10 +249,10 @@ float Line2D::get_sharp_limit() const {
return _sharp_limit;
}
void Line2D::set_round_precision(int precision) {
if (precision < 1)
precision = 1;
_round_precision = precision;
void Line2D::set_round_precision(int p_precision) {
if (p_precision < 1)
p_precision = 1;
_round_precision = p_precision;
update();
}
@ -268,10 +288,11 @@ void Line2D::_draw() {
lb.round_precision = _round_precision;
lb.sharp_limit = _sharp_limit;
lb.width = _width;
lb.curve = *_curve;
RID texture_rid;
if (_texture.is_valid()) {
texture_rid = (**_texture).get_rid();
texture_rid = _texture->get_rid();
lb.tile_aspect = _texture->get_size().aspect();
}
@ -312,6 +333,10 @@ void Line2D::_gradient_changed() {
update();
}
void Line2D::_curve_changed() {
update();
}
// static
void Line2D::_bind_methods() {
@ -331,6 +356,9 @@ void Line2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_width", "width"), &Line2D::set_width);
ClassDB::bind_method(D_METHOD("get_width"), &Line2D::get_width);
ClassDB::bind_method(D_METHOD("set_curve", "curve"), &Line2D::set_curve);
ClassDB::bind_method(D_METHOD("get_curve"), &Line2D::get_curve);
ClassDB::bind_method(D_METHOD("set_default_color", "color"), &Line2D::set_default_color);
ClassDB::bind_method(D_METHOD("get_default_color"), &Line2D::get_default_color);
@ -360,6 +388,7 @@ void Line2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "points"), "set_points", "get_points");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "width"), "set_width", "get_width");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "width_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "default_color"), "set_default_color", "get_default_color");
ADD_GROUP("Fill", "");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_gradient", "get_gradient");
@ -386,4 +415,5 @@ void Line2D::_bind_methods() {
BIND_ENUM_CONSTANT(LINE_TEXTURE_STRETCH);
ClassDB::bind_method(D_METHOD("_gradient_changed"), &Line2D::_gradient_changed);
ClassDB::bind_method(D_METHOD("_curve_changed"), &Line2D::_curve_changed);
}

View File

@ -78,6 +78,9 @@ public:
void set_width(float width);
float get_width() const;
void set_curve(const Ref<Curve> &curve);
Ref<Curve> get_curve() const;
void set_default_color(Color color);
Color get_default_color() const;
@ -113,6 +116,7 @@ protected:
private:
void _gradient_changed();
void _curve_changed();
private:
PoolVector<Vector2> _points;
@ -120,6 +124,7 @@ private:
LineCapMode _begin_cap_mode;
LineCapMode _end_cap_mode;
float _width;
Ref<Curve> _curve;
Color _default_color;
Ref<Gradient> _gradient;
Ref<Texture> _texture;

View File

@ -95,6 +95,7 @@ static inline Vector2 interpolate(const Rect2 &r, const Vector2 &v) {
LineBuilder::LineBuilder() {
joint_mode = Line2D::LINE_JOINT_SHARP;
width = 10;
curve = NULL;
default_color = Color(0.4, 0.5, 1);
gradient = NULL;
sharp_limit = 2.f;
@ -136,8 +137,8 @@ void LineBuilder::build() {
Vector2 pos1 = points[1];
Vector2 f0 = (pos1 - pos0).normalized();
Vector2 u0 = rotate90(f0);
Vector2 pos_up0 = pos0 + u0 * hw;
Vector2 pos_down0 = pos0 - u0 * hw;
Vector2 pos_up0 = pos0;
Vector2 pos_down0 = pos0;
Color color0;
Color color1;
@ -145,12 +146,30 @@ void LineBuilder::build() {
float current_distance0 = 0.f;
float current_distance1 = 0.f;
float total_distance = 0.f;
float width_factor = 1.f;
_interpolate_color = gradient != NULL;
bool retrieve_curve = curve != NULL;
bool distance_required = _interpolate_color ||
retrieve_curve ||
texture_mode == Line2D::LINE_TEXTURE_TILE ||
texture_mode == Line2D::LINE_TEXTURE_STRETCH;
if (distance_required)
if (distance_required) {
total_distance = calculate_total_distance(points);
//Ajust totalDistance.
// The line's outer length will be a little higher due to begin and end caps
if (begin_cap_mode == Line2D::LINE_CAP_BOX || begin_cap_mode == Line2D::LINE_CAP_ROUND) {
if (retrieve_curve)
total_distance += width * curve->interpolate_baked(0.f) * 0.5f;
else
total_distance += width * 0.5f;
}
if (end_cap_mode == Line2D::LINE_CAP_BOX || end_cap_mode == Line2D::LINE_CAP_ROUND) {
if (retrieve_curve)
total_distance += width * curve->interpolate_baked(1.f) * 0.5f;
else
total_distance += width * 0.5f;
}
}
if (_interpolate_color)
color0 = gradient->get_color(0);
else
@ -159,22 +178,28 @@ void LineBuilder::build() {
float uvx0 = 0.f;
float uvx1 = 0.f;
if (retrieve_curve)
width_factor = curve->interpolate_baked(0.f);
pos_up0 += u0 * hw * width_factor;
pos_down0 -= u0 * hw * width_factor;
// Begin cap
if (begin_cap_mode == Line2D::LINE_CAP_BOX) {
// Push back first vertices a little bit
pos_up0 -= f0 * hw;
pos_down0 -= f0 * hw;
// The line's outer length will be a little higher due to begin and end caps
total_distance += width;
current_distance0 += hw;
pos_up0 -= f0 * hw * width_factor;
pos_down0 -= f0 * hw * width_factor;
current_distance0 += hw * width_factor;
current_distance1 = current_distance0;
} else if (begin_cap_mode == Line2D::LINE_CAP_ROUND) {
if (texture_mode == Line2D::LINE_TEXTURE_TILE) {
uvx0 = 0.5f / tile_aspect;
uvx0 = width_factor * 0.5f / tile_aspect;
} else if (texture_mode == Line2D::LINE_TEXTURE_STRETCH) {
uvx0 = width * width_factor / total_distance;
}
new_arc(pos0, pos_up0 - pos0, -Math_PI, color0, Rect2(0.f, 0.f, fmin(uvx0 * 2, 1.f), 1.f));
total_distance += width;
current_distance0 += hw;
new_arc(pos0, pos_up0 - pos0, -Math_PI, color0, Rect2(0.f, 0.f, uvx0 * 2, 1.f));
current_distance0 += hw * width_factor;
current_distance1 = current_distance0;
}
@ -206,13 +231,23 @@ void LineBuilder::build() {
const float dp = u0.dot(f1);
const Orientation orientation = (dp > 0.f ? UP : DOWN);
if (distance_required) {
current_distance1 += pos0.distance_to(pos1);
}
if (_interpolate_color) {
color1 = gradient->get_color_at_offset(current_distance1 / total_distance);
}
if (retrieve_curve) {
width_factor = curve->interpolate_baked(current_distance1 / total_distance);
}
Vector2 inner_normal0, inner_normal1;
if (orientation == UP) {
inner_normal0 = u0 * hw;
inner_normal1 = u1 * hw;
inner_normal0 = u0 * hw * width_factor;
inner_normal1 = u1 * hw * width_factor;
} else {
inner_normal0 = -u0 * hw;
inner_normal1 = -u1 * hw;
inner_normal0 = -u0 * hw * width_factor;
inner_normal1 = -u1 * hw * width_factor;
}
/*
@ -259,7 +294,8 @@ void LineBuilder::build() {
Vector2 pos_up1, pos_down1;
if (intersection_result == SEGMENT_INTERSECT) {
// Fallback on bevel if sharp angle is too high (because it would produce very long miters)
if (current_joint_mode == Line2D::LINE_JOINT_SHARP && corner_pos_out.distance_squared_to(pos1) / hw_sq > sharp_limit_sq) {
float width_factor_sq = width_factor * width_factor;
if (current_joint_mode == Line2D::LINE_JOINT_SHARP && corner_pos_out.distance_squared_to(pos1) / (hw_sq * width_factor_sq) > sharp_limit_sq) {
current_joint_mode = Line2D::LINE_JOINT_BEVEL;
}
if (current_joint_mode == Line2D::LINE_JOINT_SHARP) {
@ -271,9 +307,9 @@ void LineBuilder::build() {
// Bevel or round
if (orientation == UP) {
pos_up1 = corner_pos_up;
pos_down1 = pos1 - u0 * hw;
pos_down1 = pos1 - u0 * hw * width_factor;
} else {
pos_up1 = pos1 + u0 * hw;
pos_up1 = pos1 + u0 * hw * width_factor;
pos_down1 = corner_pos_down;
}
}
@ -289,12 +325,6 @@ void LineBuilder::build() {
// Add current line body quad
// Triangles are clockwise
if (distance_required) {
current_distance1 += pos0.distance_to(pos1);
}
if (_interpolate_color) {
color1 = gradient->get_color_at_offset(current_distance1 / total_distance);
}
if (texture_mode == Line2D::LINE_TEXTURE_TILE) {
uvx1 = current_distance1 / (width * tile_aspect);
} else if (texture_mode == Line2D::LINE_TEXTURE_STRETCH) {
@ -315,15 +345,15 @@ void LineBuilder::build() {
} else {
if (orientation == UP) {
pos_up0 = corner_pos_up;
pos_down0 = pos1 - u1 * hw;
pos_down0 = pos1 - u1 * hw * width_factor;
} else {
pos_up0 = pos1 + u1 * hw;
pos_up0 = pos1 + u1 * hw * width_factor;
pos_down0 = corner_pos_down;
}
}
} else {
pos_up0 = pos1 + u1 * hw;
pos_down0 = pos1 - u1 * hw;
pos_up0 = pos1 + u1 * hw * width_factor;
pos_down0 = pos1 - u1 * hw * width_factor;
}
// From this point, bu0 and bd0 concern the next segment
@ -362,26 +392,28 @@ void LineBuilder::build() {
strip_begin(pos_up0, pos_down0, color1, uvx1);
}
}
// Last (or only) segment
pos1 = points[points.size() - 1];
Vector2 pos_up1 = pos1 + u0 * hw;
Vector2 pos_down1 = pos1 - u0 * hw;
// End cap (box)
if (end_cap_mode == Line2D::LINE_CAP_BOX) {
pos_up1 += f0 * hw;
pos_down1 += f0 * hw;
}
if (distance_required) {
current_distance1 += pos0.distance_to(pos1);
}
if (_interpolate_color) {
color1 = gradient->get_color(gradient->get_points_count() - 1);
}
if (retrieve_curve) {
width_factor = curve->interpolate_baked(1.f);
}
Vector2 pos_up1 = pos1 + u0 * hw * width_factor;
Vector2 pos_down1 = pos1 - u0 * hw * width_factor;
// End cap (box)
if (end_cap_mode == Line2D::LINE_CAP_BOX) {
pos_up1 += f0 * hw * width_factor;
pos_down1 += f0 * hw * width_factor;
}
if (texture_mode == Line2D::LINE_TEXTURE_TILE) {
uvx1 = current_distance1 / (width * tile_aspect);
} else if (texture_mode == Line2D::LINE_TEXTURE_STRETCH) {
@ -394,7 +426,13 @@ void LineBuilder::build() {
if (end_cap_mode == Line2D::LINE_CAP_ROUND) {
// Note: color is not used in case we don't interpolate...
Color color = _interpolate_color ? gradient->get_color(gradient->get_points_count() - 1) : Color(0, 0, 0);
new_arc(pos1, pos_up1 - pos1, Math_PI, color, Rect2(uvx1 - 0.5f / tile_aspect, 0.f, 1.0f / tile_aspect, 1.f));
float dist = 0;
if (texture_mode == Line2D::LINE_TEXTURE_TILE) {
dist = width_factor / tile_aspect;
} else if (texture_mode == Line2D::LINE_TEXTURE_STRETCH) {
dist = width * width_factor / total_distance;
}
new_arc(pos1, pos_up1 - pos1, Math_PI, color, Rect2(uvx1 - 0.5f * dist, 0.f, dist, 1.f));
}
}

View File

@ -45,6 +45,7 @@ public:
Line2D::LineCapMode begin_cap_mode;
Line2D::LineCapMode end_cap_mode;
float width;
Curve *curve;
Color default_color;
Gradient *gradient;
Line2D::LineTextureMode texture_mode;