|
@@ -1454,6 +1454,9 @@ void Curve3D::_remove_point(int p_index) {
|
|
|
|
|
|
void Curve3D::remove_point(int p_index) {
|
|
|
_remove_point(p_index);
|
|
|
+ if (closed && points.size() < 2) {
|
|
|
+ set_closed(false);
|
|
|
+ }
|
|
|
notify_property_list_changed();
|
|
|
}
|
|
|
|
|
@@ -1470,15 +1473,25 @@ Vector3 Curve3D::sample(int p_index, real_t p_offset) const {
|
|
|
ERR_FAIL_COND_V(pc == 0, Vector3());
|
|
|
|
|
|
if (p_index >= pc - 1) {
|
|
|
- return points[pc - 1].position;
|
|
|
+ if (!closed) {
|
|
|
+ return points[pc - 1].position;
|
|
|
+ } else {
|
|
|
+ p_index = pc - 1;
|
|
|
+ }
|
|
|
} else if (p_index < 0) {
|
|
|
return points[0].position;
|
|
|
}
|
|
|
|
|
|
Vector3 p0 = points[p_index].position;
|
|
|
Vector3 p1 = p0 + points[p_index].out;
|
|
|
- Vector3 p3 = points[p_index + 1].position;
|
|
|
- Vector3 p2 = p3 + points[p_index + 1].in;
|
|
|
+ Vector3 p3, p2;
|
|
|
+ if (!closed || p_index < pc - 1) {
|
|
|
+ p3 = points[p_index + 1].position;
|
|
|
+ p2 = p3 + points[p_index + 1].in;
|
|
|
+ } else {
|
|
|
+ p3 = points[0].position;
|
|
|
+ p2 = p3 + points[0].in;
|
|
|
+ }
|
|
|
|
|
|
return p0.bezier_interpolate(p1, p2, p3, p_offset);
|
|
|
}
|
|
@@ -1596,13 +1609,16 @@ void Curve3D::_bake() const {
|
|
|
{
|
|
|
Vector<RBMap<real_t, Vector3>> midpoints = _tessellate_even_length(10, bake_interval);
|
|
|
|
|
|
+ const int num_intervals = closed ? points.size() : points.size() - 1;
|
|
|
+
|
|
|
#ifdef TOOLS_ENABLED
|
|
|
- points_in_cache.resize(points.size());
|
|
|
+ points_in_cache.resize(closed ? (points.size() + 1) : points.size());
|
|
|
points_in_cache.set(0, 0);
|
|
|
#endif
|
|
|
|
|
|
+ // Point Count: Begins at 1 to account for the last point.
|
|
|
int pc = 1;
|
|
|
- for (int i = 0; i < points.size() - 1; i++) {
|
|
|
+ for (int i = 0; i < num_intervals; i++) {
|
|
|
pc++;
|
|
|
pc += midpoints[i].size();
|
|
|
#ifdef TOOLS_ENABLED
|
|
@@ -1625,18 +1641,29 @@ void Curve3D::_bake() const {
|
|
|
btw[0] = points[0].tilt;
|
|
|
int pidx = 0;
|
|
|
|
|
|
- for (int i = 0; i < points.size() - 1; i++) {
|
|
|
+ for (int i = 0; i < num_intervals; i++) {
|
|
|
for (const KeyValue<real_t, Vector3> &E : midpoints[i]) {
|
|
|
pidx++;
|
|
|
bpw[pidx] = E.value;
|
|
|
- bfw[pidx] = _calculate_tangent(points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, E.key);
|
|
|
- btw[pidx] = Math::lerp(points[i].tilt, points[i + 1].tilt, E.key);
|
|
|
+ if (!closed || i < num_intervals - 1) {
|
|
|
+ bfw[pidx] = _calculate_tangent(points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, E.key);
|
|
|
+ btw[pidx] = Math::lerp(points[i].tilt, points[i + 1].tilt, E.key);
|
|
|
+ } else {
|
|
|
+ bfw[pidx] = _calculate_tangent(points[i].position, points[i].position + points[i].out, points[0].position + points[0].in, points[0].position, E.key);
|
|
|
+ btw[pidx] = Math::lerp(points[i].tilt, points[0].tilt, E.key);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
pidx++;
|
|
|
- bpw[pidx] = points[i + 1].position;
|
|
|
- bfw[pidx] = _calculate_tangent(points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, 1.0);
|
|
|
- btw[pidx] = points[i + 1].tilt;
|
|
|
+ if (!closed || i < num_intervals - 1) {
|
|
|
+ bpw[pidx] = points[i + 1].position;
|
|
|
+ bfw[pidx] = _calculate_tangent(points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, 1.0);
|
|
|
+ btw[pidx] = points[i + 1].tilt;
|
|
|
+ } else {
|
|
|
+ bpw[pidx] = points[0].position;
|
|
|
+ bfw[pidx] = _calculate_tangent(points[i].position, points[i].position + points[i].out, points[0].position + points[0].in, points[0].position, 1.0);
|
|
|
+ btw[pidx] = points[0].tilt;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// Recalculate the baked distances.
|
|
@@ -2075,6 +2102,20 @@ real_t Curve3D::get_closest_offset(const Vector3 &p_to_point) const {
|
|
|
return nearest;
|
|
|
}
|
|
|
|
|
|
+void Curve3D::set_closed(bool p_closed) {
|
|
|
+ if (closed == p_closed) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ closed = p_closed;
|
|
|
+ mark_dirty();
|
|
|
+ notify_property_list_changed();
|
|
|
+}
|
|
|
+
|
|
|
+bool Curve3D::is_closed() const {
|
|
|
+ return closed;
|
|
|
+}
|
|
|
+
|
|
|
void Curve3D::set_bake_interval(real_t p_tolerance) {
|
|
|
bake_interval = p_tolerance;
|
|
|
mark_dirty();
|
|
@@ -2153,11 +2194,17 @@ PackedVector3Array Curve3D::tessellate(int p_max_stages, real_t p_tolerance) con
|
|
|
}
|
|
|
Vector<RBMap<real_t, Vector3>> midpoints;
|
|
|
|
|
|
- midpoints.resize(points.size() - 1);
|
|
|
+ const int num_intervals = closed ? points.size() : points.size() - 1;
|
|
|
+ midpoints.resize(num_intervals);
|
|
|
|
|
|
+ // Point Count: Begins at 1 to account for the last point.
|
|
|
int pc = 1;
|
|
|
- for (int i = 0; i < points.size() - 1; i++) {
|
|
|
- _bake_segment3d(midpoints.write[i], 0, 1, points[i].position, points[i].out, points[i + 1].position, points[i + 1].in, 0, p_max_stages, p_tolerance);
|
|
|
+ for (int i = 0; i < num_intervals; i++) {
|
|
|
+ if (!closed || i < num_intervals - 1) {
|
|
|
+ _bake_segment3d(midpoints.write[i], 0, 1, points[i].position, points[i].out, points[i + 1].position, points[i + 1].in, 0, p_max_stages, p_tolerance);
|
|
|
+ } else {
|
|
|
+ _bake_segment3d(midpoints.write[i], 0, 1, points[i].position, points[i].out, points[0].position, points[0].in, 0, p_max_stages, p_tolerance);
|
|
|
+ }
|
|
|
pc++;
|
|
|
pc += midpoints[i].size();
|
|
|
}
|
|
@@ -2167,14 +2214,18 @@ PackedVector3Array Curve3D::tessellate(int p_max_stages, real_t p_tolerance) con
|
|
|
bpw[0] = points[0].position;
|
|
|
int pidx = 0;
|
|
|
|
|
|
- for (int i = 0; i < points.size() - 1; i++) {
|
|
|
+ for (int i = 0; i < num_intervals; i++) {
|
|
|
for (const KeyValue<real_t, Vector3> &E : midpoints[i]) {
|
|
|
pidx++;
|
|
|
bpw[pidx] = E.value;
|
|
|
}
|
|
|
|
|
|
pidx++;
|
|
|
- bpw[pidx] = points[i + 1].position;
|
|
|
+ if (!closed || i < num_intervals - 1) {
|
|
|
+ bpw[pidx] = points[i + 1].position;
|
|
|
+ } else {
|
|
|
+ bpw[pidx] = points[0].position;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
return tess;
|
|
@@ -2184,10 +2235,15 @@ Vector<RBMap<real_t, Vector3>> Curve3D::_tessellate_even_length(int p_max_stages
|
|
|
Vector<RBMap<real_t, Vector3>> midpoints;
|
|
|
ERR_FAIL_COND_V_MSG(points.size() < 2, midpoints, "Curve must have at least 2 control point");
|
|
|
|
|
|
- midpoints.resize(points.size() - 1);
|
|
|
+ const int num_intervals = closed ? points.size() : points.size() - 1;
|
|
|
+ midpoints.resize(num_intervals);
|
|
|
|
|
|
- for (int i = 0; i < points.size() - 1; i++) {
|
|
|
- _bake_segment3d_even_length(midpoints.write[i], 0, 1, points[i].position, points[i].out, points[i + 1].position, points[i + 1].in, 0, p_max_stages, p_length);
|
|
|
+ for (int i = 0; i < num_intervals; i++) {
|
|
|
+ if (!closed || i < num_intervals - 1) {
|
|
|
+ _bake_segment3d_even_length(midpoints.write[i], 0, 1, points[i].position, points[i].out, points[i + 1].position, points[i + 1].in, 0, p_max_stages, p_length);
|
|
|
+ } else {
|
|
|
+ _bake_segment3d_even_length(midpoints.write[i], 0, 1, points[i].position, points[i].out, points[0].position, points[0].in, 0, p_max_stages, p_length);
|
|
|
+ }
|
|
|
}
|
|
|
return midpoints;
|
|
|
}
|
|
@@ -2200,8 +2256,10 @@ PackedVector3Array Curve3D::tessellate_even_length(int p_max_stages, real_t p_le
|
|
|
return tess;
|
|
|
}
|
|
|
|
|
|
+ const int num_intervals = closed ? points.size() : points.size() - 1;
|
|
|
+ // Point Count: Begins at 1 to account for the last point.
|
|
|
int pc = 1;
|
|
|
- for (int i = 0; i < points.size() - 1; i++) {
|
|
|
+ for (int i = 0; i < num_intervals; i++) {
|
|
|
pc++;
|
|
|
pc += midpoints[i].size();
|
|
|
}
|
|
@@ -2211,14 +2269,18 @@ PackedVector3Array Curve3D::tessellate_even_length(int p_max_stages, real_t p_le
|
|
|
bpw[0] = points[0].position;
|
|
|
int pidx = 0;
|
|
|
|
|
|
- for (int i = 0; i < points.size() - 1; i++) {
|
|
|
+ for (int i = 0; i < num_intervals; i++) {
|
|
|
for (const KeyValue<real_t, Vector3> &E : midpoints[i]) {
|
|
|
pidx++;
|
|
|
bpw[pidx] = E.value;
|
|
|
}
|
|
|
|
|
|
pidx++;
|
|
|
- bpw[pidx] = points[i + 1].position;
|
|
|
+ if (!closed || i < num_intervals - 1) {
|
|
|
+ bpw[pidx] = points[i + 1].position;
|
|
|
+ } else {
|
|
|
+ bpw[pidx] = points[0].position;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
return tess;
|
|
@@ -2274,13 +2336,13 @@ void Curve3D::_get_property_list(List<PropertyInfo> *p_list) const {
|
|
|
pi.usage &= ~PROPERTY_USAGE_STORAGE;
|
|
|
p_list->push_back(pi);
|
|
|
|
|
|
- if (i != 0) {
|
|
|
+ if (closed || i != 0) {
|
|
|
pi = PropertyInfo(Variant::VECTOR3, vformat("point_%d/in", i));
|
|
|
pi.usage &= ~PROPERTY_USAGE_STORAGE;
|
|
|
p_list->push_back(pi);
|
|
|
}
|
|
|
|
|
|
- if (i != points.size() - 1) {
|
|
|
+ if (closed || i != points.size() - 1) {
|
|
|
pi = PropertyInfo(Variant::VECTOR3, vformat("point_%d/out", i));
|
|
|
pi.usage &= ~PROPERTY_USAGE_STORAGE;
|
|
|
p_list->push_back(pi);
|
|
@@ -2308,6 +2370,8 @@ void Curve3D::_bind_methods() {
|
|
|
ClassDB::bind_method(D_METHOD("clear_points"), &Curve3D::clear_points);
|
|
|
ClassDB::bind_method(D_METHOD("sample", "idx", "t"), &Curve3D::sample);
|
|
|
ClassDB::bind_method(D_METHOD("samplef", "fofs"), &Curve3D::samplef);
|
|
|
+ ClassDB::bind_method(D_METHOD("set_closed", "closed"), &Curve3D::set_closed);
|
|
|
+ ClassDB::bind_method(D_METHOD("is_closed"), &Curve3D::is_closed);
|
|
|
//ClassDB::bind_method(D_METHOD("bake","subdivs"),&Curve3D::bake,DEFVAL(10));
|
|
|
ClassDB::bind_method(D_METHOD("set_bake_interval", "distance"), &Curve3D::set_bake_interval);
|
|
|
ClassDB::bind_method(D_METHOD("get_bake_interval"), &Curve3D::get_bake_interval);
|
|
@@ -2329,6 +2393,8 @@ void Curve3D::_bind_methods() {
|
|
|
ClassDB::bind_method(D_METHOD("_get_data"), &Curve3D::_get_data);
|
|
|
ClassDB::bind_method(D_METHOD("_set_data", "data"), &Curve3D::_set_data);
|
|
|
|
|
|
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "closed"), "set_closed", "is_closed");
|
|
|
+
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_interval", PROPERTY_HINT_RANGE, "0.01,512,0.01"), "set_bake_interval", "get_bake_interval");
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
|
|
|
ADD_ARRAY_COUNT("Points", "point_count", "set_point_count", "get_point_count", "point_");
|