@@ -10,7 +10,7 @@ const Vector2 VECTOR_ZERO = Vector2(); // NOLINT(cert-err58-cpp)
1010const Vector2 VECTOR_DOWN = Vector2(0 , 1 ); // NOLINT(cert-err58-cpp)
1111
1212
13- static float get_point_perc (int index, const PackedVector2Array& points)
13+ static float get_point_perc (int64_t index, const PackedVector2Array& points)
1414{
1515 return points.size () > 0 ? static_cast <float >(index) / static_cast <float >(points.size () - 1 ) : 0 ;
1616}
@@ -54,6 +54,9 @@ void NativeRopeContext::load_context(Node2D* rope)
5454 wind = rope->get (" wind" );
5555 damping = rope->get (" damping" );
5656 stiffness = rope->get (" stiffness" );
57+ stiffness_method = static_cast <StiffnessMethod>(static_cast <int >(rope->get (" stiffness_method" )));
58+ stiffness_curve = rope->get (" stiffness_curve" );
59+ stiffness_bend_curve = rope->get (" stiffness_bend_curve" );
5760 max_endpoint_distance = rope->get (" max_endpoint_distance" );
5861 num_constraint_iterations = rope->get (" num_constraint_iterations" );
5962 seg_lengths = rope->call (" get_segment_lengths" );
@@ -132,7 +135,7 @@ void NativeRopeContext::_simulate_velocities(double delta)
132135
133136 // Apply other forces
134137 _simulate_wind (&velocities);
135- _simulate_stiffness (&velocities);
138+ _simulate_stiffness (delta, &velocities);
136139
137140 // Move points according to velocity
138141 for (int i = first_idx; i < size; ++i)
@@ -158,46 +161,83 @@ void NativeRopeContext::_simulate_wind(PackedVector2Array* velocities) const
158161 }
159162}
160163
161- void NativeRopeContext::_simulate_stiffness (PackedVector2Array* velocities) const
164+ void NativeRopeContext::_simulate_stiffness (double delta, PackedVector2Array* velocities) const
162165{
163- // NOTE: oldpoints should not be used here, see comments in simulate_velocities().
164-
165166 if (stiffness <= 0 )
166167 return ;
167168
168- Vector2 parent_seg_dir = rope->get_global_transform ().basis_xform (VECTOR_DOWN).normalized ();
169- Vector2 last_stiffness_force;
169+ const auto num_points = points.size ();
170+
171+ if (stiffness_method == StiffnessMethod::Legacy)
172+ {
173+ const Vector2 parent_seg_dir = rope->get_global_transform ().basis_xform (VECTOR_DOWN).normalized ();
174+ _simulate_stiffness_segment (delta, velocities, 1 , num_points, parent_seg_dir); // Start at second and stop at last point
175+ }
176+ else
177+ {
178+ const Vector2 end_parent = (points[num_points - 2 ] - points[num_points - 1 ]).normalized ();
179+
180+ if (fixate_begin)
181+ {
182+ // TODO: Make direction configurable, e.g. an enum to select the mode ("transform", "gravity", "custom").
183+ const Vector2 parent_seg_dir = rope->get_global_transform ().basis_xform (VECTOR_DOWN).normalized ();
184+ _simulate_stiffness_segment (delta, velocities, 1 , num_points, parent_seg_dir); // Start at second and stop at last point
185+ _simulate_stiffness_segment (delta, velocities, num_points - 2 , 0 , end_parent); // Start at second last and stop before first point (because it's fixed)
186+ }
187+ else
188+ {
189+ const Vector2 begin_parent = (points[1 ] - points[0 ]).normalized ();
190+ _simulate_stiffness_segment (delta, velocities, 1 , num_points, begin_parent); // Start at second and stop at last point
191+ _simulate_stiffness_segment (delta, velocities, num_points - 2 , -1 , end_parent); // Start at second last and stop at first point
192+ }
193+ }
194+ }
195+
196+ void NativeRopeContext::_simulate_stiffness_segment (double /* delta*/ , PackedVector2Array* velocities, int64_t idx_from, int64_t idx_stop, Vector2 last_stiffness_dir) const
197+ {
198+ // NOTE: oldpoints should not be used here, see comments in simulate_velocities().
170199
171- for (int i = 1 ; i < points.size (); ++i)
200+ Vector2 force;
201+ const bool use_stiffness_curve = stiffness_curve.is_valid () && stiffness_curve->get_point_count () > 0 ;
202+ const bool use_bend_curve = stiffness_bend_curve.is_valid () && stiffness_bend_curve->get_point_count () > 0 ;
203+ const int64_t idx_step = idx_from <= idx_stop ? 1 : -1 ;
204+
205+ for (int64_t i = idx_from; i != idx_stop; i += idx_step)
172206 {
173207 // NOTE: Asked a physicist to confirm this computation is physically accurate.
174208 // He mentioned that, while it is technically correct, there is a material-dependent limit
175209 // how far an object can bend before bending properties (stiffness) changes.
176210 // E.g. a material might be less inclined to snap back into place at smaller bend angles, or
177211 // a material might stop bending at some point, i.e. when it breaks.
178- // This implementation should probably suffice in most cases, but a more advanced
179- // implementation would include curves for stiffness in relation to segment position and
180- // for stiffness in relation to bend angle.
181212 //
182213 // | parent_seg_dir ---> parent_seg_dir.orthogonal()
183214 // | \
184215 // V \ seg_dir
185216 // \ seg_dir V
186- // \
187- // V
188- const Vector2 seg_dir = (points[i] - points[i - 1 ]).normalized ();
189- const float angle = seg_dir.angle_to (parent_seg_dir);
217+ // \ / force_dir
218+ // V V
219+ // / force_dir
220+ // V
221+ //
222+ const auto last_idx = i - idx_step;
223+ const Vector2 seg_dir = (points[i] - points[last_idx]).normalized ();
224+ const float angle = seg_dir.angle_to (last_stiffness_dir); // angle is signed and can be used to determine the force direction
225+ const float bend_factor = Math::absf (angle / 3 .1415f );
226+ const float scale_curve_pos = get_point_perc (idx_step >= 0 ? i : (points.size () - 1 - i), points);
227+ const float scale = use_stiffness_curve ? stiffness_curve->sample_baked (scale_curve_pos) : 1 .0f ;
228+ // If no curve specified, use quadratic scale by default
229+ const float bend_scale = (use_bend_curve ? stiffness_bend_curve->sample_baked (bend_factor) : bend_factor * bend_factor);
190230
191231 // The force directs orthogonal to the current segment
192- const Vector2 force_dir = seg_dir.orthogonal ();
232+ const Vector2 force_dir = Math::sign (-angle) * seg_dir.orthogonal ();
233+ force += force_dir * stiffness * scale * bend_scale;
193234
194- // Scale the force the further the segment bends.
195- // angle is signed and can be used to determine the force direction
196- last_stiffness_force += force_dir * (-angle / 3 .1415f ) * stiffness;
197- parent_seg_dir = seg_dir;
235+ // `simulation_weights` will be applied later in `_simulate_velocities()`
236+ (*velocities)[i] += force;
198237
199- // Update velocity
200- (*velocities)[i] += last_stiffness_force;
238+ // Carry velocity over to the next segment. `simulation_weights` must be applied now to not carry over a wrong value.
239+ force *= simulation_weights[i];
240+ last_stiffness_dir = seg_dir;
201241 }
202242}
203243
0 commit comments