Added a Width Curve to Line2D + UVs fix

This commit is contained in:
Tristan Grespinet 2019-05-01 06:50:01 +02:00
parent dd2cd06165
commit 14f8ed3317
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"> <member name="width" type="float" setter="set_width" getter="get_width">
The line's width. The line's width.
</member> </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> </members>
<constants> <constants>
<constant name="LINE_JOINT_SHARP" value="0" enum="LineJointMode"> <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(); update();
} }
void Line2D::set_width(float width) { void Line2D::set_width(float p_width) {
if (width < 0.0) if (p_width < 0.0)
width = 0.0; p_width = 0.0;
_width = width; _width = p_width;
update(); update();
} }
@ -95,12 +95,32 @@ float Line2D::get_width() const {
return _width; 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 { PoolVector<Vector2> Line2D::get_points() const {
return _points; return _points;
} }
void Line2D::set_point_position(int i, Vector2 pos) { void Line2D::set_point_position(int i, Vector2 p_pos) {
_points.set(i, pos); _points.set(i, p_pos);
update(); update();
} }
@ -120,11 +140,11 @@ void Line2D::clear_points() {
} }
} }
void Line2D::add_point(Vector2 pos, int atpos) { void Line2D::add_point(Vector2 p_pos, int p_atpos) {
if (atpos < 0 || _points.size() < atpos) { if (p_atpos < 0 || _points.size() < p_atpos) {
_points.append(pos); _points.append(p_pos);
} else { } else {
_points.insert(atpos, pos); _points.insert(p_atpos, p_pos);
} }
update(); update();
} }
@ -134,8 +154,8 @@ void Line2D::remove_point(int i) {
update(); update();
} }
void Line2D::set_default_color(Color color) { void Line2D::set_default_color(Color p_color) {
_default_color = color; _default_color = p_color;
update(); update();
} }
@ -143,18 +163,18 @@ Color Line2D::get_default_color() const {
return _default_color; 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 // Cleanup previous connection if any
if (_gradient.is_valid()) { 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 // Connect to the gradient so the line will update when the ColorRamp is changed
if (_gradient.is_valid()) { if (_gradient.is_valid()) {
(**_gradient).connect(CoreStringNames::get_singleton()->changed, this, "_gradient_changed"); _gradient->connect(CoreStringNames::get_singleton()->changed, this, "_gradient_changed");
} }
update(); update();
@ -164,8 +184,8 @@ Ref<Gradient> Line2D::get_gradient() const {
return _gradient; return _gradient;
} }
void Line2D::set_texture(const Ref<Texture> &texture) { void Line2D::set_texture(const Ref<Texture> &p_texture) {
_texture = texture; _texture = p_texture;
update(); update();
} }
@ -173,8 +193,8 @@ Ref<Texture> Line2D::get_texture() const {
return _texture; return _texture;
} }
void Line2D::set_texture_mode(const LineTextureMode mode) { void Line2D::set_texture_mode(const LineTextureMode p_mode) {
_texture_mode = mode; _texture_mode = p_mode;
update(); update();
} }
@ -182,8 +202,8 @@ Line2D::LineTextureMode Line2D::get_texture_mode() const {
return _texture_mode; return _texture_mode;
} }
void Line2D::set_joint_mode(LineJointMode mode) { void Line2D::set_joint_mode(LineJointMode p_mode) {
_joint_mode = mode; _joint_mode = p_mode;
update(); update();
} }
@ -191,8 +211,8 @@ Line2D::LineJointMode Line2D::get_joint_mode() const {
return _joint_mode; return _joint_mode;
} }
void Line2D::set_begin_cap_mode(LineCapMode mode) { void Line2D::set_begin_cap_mode(LineCapMode p_mode) {
_begin_cap_mode = mode; _begin_cap_mode = p_mode;
update(); update();
} }
@ -200,8 +220,8 @@ Line2D::LineCapMode Line2D::get_begin_cap_mode() const {
return _begin_cap_mode; return _begin_cap_mode;
} }
void Line2D::set_end_cap_mode(LineCapMode mode) { void Line2D::set_end_cap_mode(LineCapMode p_mode) {
_end_cap_mode = mode; _end_cap_mode = p_mode;
update(); update();
} }
@ -217,10 +237,10 @@ void Line2D::_notification(int p_what) {
} }
} }
void Line2D::set_sharp_limit(float limit) { void Line2D::set_sharp_limit(float p_limit) {
if (limit < 0.f) if (p_limit < 0.f)
limit = 0.f; p_limit = 0.f;
_sharp_limit = limit; _sharp_limit = p_limit;
update(); update();
} }
@ -228,10 +248,10 @@ float Line2D::get_sharp_limit() const {
return _sharp_limit; return _sharp_limit;
} }
void Line2D::set_round_precision(int precision) { void Line2D::set_round_precision(int p_precision) {
if (precision < 1) if (p_precision < 1)
precision = 1; p_precision = 1;
_round_precision = precision; _round_precision = p_precision;
update(); update();
} }
@ -267,10 +287,11 @@ void Line2D::_draw() {
lb.round_precision = _round_precision; lb.round_precision = _round_precision;
lb.sharp_limit = _sharp_limit; lb.sharp_limit = _sharp_limit;
lb.width = _width; lb.width = _width;
lb.curve = *_curve;
RID texture_rid; RID texture_rid;
if (_texture.is_valid()) { if (_texture.is_valid()) {
texture_rid = (**_texture).get_rid(); texture_rid = _texture->get_rid();
lb.tile_aspect = _texture->get_size().aspect(); lb.tile_aspect = _texture->get_size().aspect();
} }
@ -311,6 +332,10 @@ void Line2D::_gradient_changed() {
update(); update();
} }
void Line2D::_curve_changed() {
update();
}
// static // static
void Line2D::_bind_methods() { void Line2D::_bind_methods() {
@ -330,6 +355,9 @@ void Line2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_width", "width"), &Line2D::set_width); 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("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("set_default_color", "color"), &Line2D::set_default_color);
ClassDB::bind_method(D_METHOD("get_default_color"), &Line2D::get_default_color); ClassDB::bind_method(D_METHOD("get_default_color"), &Line2D::get_default_color);
@ -359,6 +387,7 @@ void Line2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::POOL_VECTOR2_ARRAY, "points"), "set_points", "get_points"); 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::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_PROPERTY(PropertyInfo(Variant::COLOR, "default_color"), "set_default_color", "get_default_color");
ADD_GROUP("Fill", ""); ADD_GROUP("Fill", "");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_gradient", "get_gradient"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_gradient", "get_gradient");
@ -385,4 +414,5 @@ void Line2D::_bind_methods() {
BIND_ENUM_CONSTANT(LINE_TEXTURE_STRETCH); BIND_ENUM_CONSTANT(LINE_TEXTURE_STRETCH);
ClassDB::bind_method(D_METHOD("_gradient_changed"), &Line2D::_gradient_changed); 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); void set_width(float width);
float get_width() const; float get_width() const;
void set_curve(const Ref<Curve> &curve);
Ref<Curve> get_curve() const;
void set_default_color(Color color); void set_default_color(Color color);
Color get_default_color() const; Color get_default_color() const;
@ -113,6 +116,7 @@ protected:
private: private:
void _gradient_changed(); void _gradient_changed();
void _curve_changed();
private: private:
PoolVector<Vector2> _points; PoolVector<Vector2> _points;
@ -120,6 +124,7 @@ private:
LineCapMode _begin_cap_mode; LineCapMode _begin_cap_mode;
LineCapMode _end_cap_mode; LineCapMode _end_cap_mode;
float _width; float _width;
Ref<Curve> _curve;
Color _default_color; Color _default_color;
Ref<Gradient> _gradient; Ref<Gradient> _gradient;
Ref<Texture> _texture; Ref<Texture> _texture;

View File

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