Jelajahi Sumber

Added a Width Curve to Line2D + UVs fix

Tristan Grespinet 6 tahun lalu
induk
melakukan
14f8ed3317
5 mengubah file dengan 153 tambahan dan 76 penghapusan
  1. 3 0
      doc/classes/Line2D.xml
  2. 65 35
      scene/2d/line_2d.cpp
  3. 5 0
      scene/2d/line_2d.h
  4. 79 41
      scene/2d/line_builder.cpp
  5. 1 0
      scene/2d/line_builder.h

+ 3 - 0
doc/classes/Line2D.xml

@@ -99,6 +99,9 @@
 		<member name="width" type="float" setter="set_width" getter="get_width">
 			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">

+ 65 - 35
scene/2d/line_2d.cpp

@@ -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();
 }
 
@@ -120,11 +140,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();
 }
@@ -134,8 +154,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();
 }
 
@@ -143,18 +163,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();
@@ -164,8 +184,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();
 }
 
@@ -173,8 +193,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();
 }
 
@@ -182,8 +202,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();
 }
 
@@ -191,8 +211,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();
 }
 
@@ -200,8 +220,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();
 }
 
@@ -217,10 +237,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();
 }
 
@@ -228,10 +248,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();
 }
 
@@ -267,10 +287,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();
 	}
@@ -311,6 +332,10 @@ void Line2D::_gradient_changed() {
 	update();
 }
 
+void Line2D::_curve_changed() {
+	update();
+}
+
 // static
 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("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);
 
@@ -359,6 +387,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");
@@ -385,4 +414,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);
 }

+ 5 - 0
scene/2d/line_2d.h

@@ -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;

+ 79 - 41
scene/2d/line_builder.cpp

@@ -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));
 	}
 }
 

+ 1 - 0
scene/2d/line_builder.h

@@ -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;