Posts: 11
Threads: 2
Joined: Jan 2018
Reputation:
0
Hello,
Thank you for the suggestion, changing the resolution does seem to help a lot although I'm concerned about the impact on collision detection which I haven't been able to test yet.
Changing the resolution also was not a perfect solution and it's still too elastic, so I was trying your suggestion of tethering everything to the fixed particle.
for (int j = rope.UsedParticles-1; j >= 0; j--){
float dist = rope.InterparticleDistance * j;
tetherBatch.AddConstraint(j,0,dist,1,1);
}
I have no idea how I'm supposed to write this but my approach above does not work because when you extend the rope with cursor the indices do not stay in order (IE the particle with index 1 stops being the next particle after the fixed particle.) How am I supposed to do this?
Additionally although I haven't been able to test either yet - why do you recommend tethering each particle to the fixed particle instead of tethering each particle to the preceding particle?
Side note for anyone reading this: advancing the coroutine that creates the rope manually with movenext was suggested in case it was mandatory to perform certain rope functions on the same frame the rope is created. Doing this is not hard but I'm hoping that by posting the code I can save someone the few hours of research and guessing it took.
IEnumerator ienumerator = rope.GeneratePhysicRepresentationForMesh();
while(!rope.Initialized) {
ienumerator.MoveNext();
}
I have not been able to observe any performance consequence / frame drop from doing this. The alternative is to, in every one of your functions that does anything with the rope, either check if rope is initialized and return if not, or set every one of them up as their own coroutine that waits for the rope to be initialized - either one of which creates huge bug potential.
Thanks,
Jon
Posts: 6,321
Threads: 24
Joined: Jun 2017
Reputation:
400
Obi Owner:
04-02-2018, 08:41 AM
(This post was last modified: 04-02-2018, 08:56 AM by josemendez.)
(04-02-2018, 03:38 AM)jonworks Wrote: Hello,
Thank you for the suggestion, changing the resolution does seem to help a lot although I'm concerned about the impact on collision detection which I haven't been able to test yet.
Changing the resolution also was not a perfect solution and it's still too elastic, so I was trying your suggestion of tethering everything to the fixed particle.
for (int j = rope.UsedParticles-1; j >= 0; j--){
float dist = rope.InterparticleDistance * j;
tetherBatch.AddConstraint(j,0,dist,1,1);
}
I have no idea how I'm supposed to write this but my approach above does not work because when you extend the rope with cursor the indices do not stay in order (IE the particle with index 1 stops being the next particle after the fixed particle.) How am I supposed to do this?
Iterate over distance constraints, as they do keep a fixed order:
Code: private void UpdateTethers()
{
rope.TetherConstraints.RemoveFromSolver(null);
ObiTetherConstraintBatch batch = (ObiTetherConstraintBatch)rope.TetherConstraints.GetBatches()[0];
batch.Clear();
ObiDistanceConstraintBatch dbatch = rope.DistanceConstraints.GetBatches()[0] as ObiDistanceConstraintBatch;
for (int i = 0; i < dbatch.ConstraintCount; ++i)
batch.AddConstraint(0,dbatch.springIndices[i*2+1], rope.InterparticleDistance*i, 1, 1);
batch.Cook();
rope.TetherConstraints.AddToSolver(null);
}
Hint: you can get Unity to draw the constraints in the scene view by enabling the "visualize" checkbox for each constraint component in the inspector. This makes it easy to debug them.
Quote:Additionally although I haven't been able to test either yet - why do you recommend tethering each particle to the fixed particle instead of tethering each particle to the preceding particle?
Because then you'd have a chain of constraints and be back to square one. As I mentioned earlier, long chains = many iterations. This is a fundamental concept when dealing with all physics engines. If you've ever tried to create a really tall stack of boxes in any engine (and failed, as it is not trivial), the underlying problem is the same: each box is constrained to the one immediately under it, and that causes a long dependency chain that needs many iterations to be solved accurately. Few iterations will cause the boxes at the bottom to sink under the weight of the ones on top of them, and the entire tower will collapse.
The whole point of tethers is that they allow you to avoid chaining them (unlike distance constraints), giving each particle in the rope their own reference point and a condition that can be met in just 1 iteration. In the box tower analogy it would be equivalent to giving each box its own "floor" to directly sit on, at the appropriate height.
I think you'd find this blog post interesting.
Quote:Side note for anyone reading this: advancing the coroutine that creates the rope manually with movenext was suggested in case it was mandatory to perform certain rope functions on the same frame the rope is created. Doing this is not hard but I'm hoping that by posting the code I can save someone the few hours of research and guessing it took.
IEnumerator ienumerator = rope.GeneratePhysicRepresentationForMesh();
while(!rope.Initialized) {
ienumerator.MoveNext();
}
I have not been able to observe any performance consequence / frame drop from doing this. The alternative is to, in every one of your functions that does anything with the rope, either check if rope is initialized and return if not, or set every one of them up as their own coroutine that waits for the rope to be initialized - either one of which creates huge bug potential.
If you read MoveNext's documentation (that I linked to), you've seen it returns a boolean value indicating whether there's more work to do. So it is usually used like this:
Code: while (enumerator.MoveNext());
No need to check if the rope is Initialized or not.
Keep in mind that for short ropes -that take very little time to initialize- the drop is not noticeable. Longer ropes however do create a bit of a hiccup. I recommend using coroutines the way they are supposed to (yield).
Posts: 11
Threads: 2
Joined: Jan 2018
Reputation:
0
Ah, now that you mention it my question about the chain of tethers was kind of stupid. For some reason I had inferred that tethers work differently from distance constraints which is obviously not the case.
I feel like if there were a direct solver for something like tethers that could be applied after the iterative solver has done its thing, this problem could be solved. All it would do is start at the fixed particle and then move the first particle toward the fixed particle such that the constraint is never violated, then move the 2nd particle toward the first particle and etc. The correction would be minuscule because it would happen every frame so a large deviation could never accumulate. Is there any way to set this up? (Possibly does the api allow direct manipulation of particle positions after the iterative solver runs?)
It's probably a little hacky and I wouldn't suggest it except that I'm really floundering trying to get this to work. When I apply tethers for each particle linked to the fixed particle the rope always wants to fold into a squiggly line and rotate around never settling down. And when I pull the rope (use cursor to reduce the length) it becomes extremely jerky like tugging the rope every few frames instead of pulling it smoothly. I've tried other tether strategies like changing the stiffness, only tethering every nth particle to the fixed particle, and even chaining them together (like tethering the 32nd particle to the 24th, the 28th to the 20th and etc). Nothing seems to give me anything other than a rubber band or a squiggly line that won't shorten with any degree of stability.
I did start by staring at Microsoft's documentation for MoveNext() that you linked to, but like most Microsoft documentation I do not have the intelligence and/or patience to derive any useful information from it. Your one line of code is much more elegant than my solution.
Again for others reading this (although I feel like I may be doing more harm than good with these...) the ObiDistanceConstraintBatch.springIndices has a really interesting structure that's not intuitive; I wrote this function to get the particle index from the position in the rope (particle_number).
private int get_particle_index(int particle_number, ObiDistanceConstraintBatch dbatch) {
if(particle_number == 0) {
return dbatch.springIndices[0];
}
else {
return dbatch.springIndices[particle_number*2-1];
}
}
Thanks,
Jon
Posts: 6,321
Threads: 24
Joined: Jun 2017
Reputation:
400
Obi Owner:
04-02-2018, 10:08 PM
(This post was last modified: 04-02-2018, 10:31 PM by josemendez.)
(04-02-2018, 08:03 PM)jonworks Wrote: I feel like if there were a direct solver for something like tethers that could be applied after the iterative solver has done its thing, this problem could be solved. All it would do is start at the fixed particle and then move the first particle toward the fixed particle such that the constraint is never violated, then move the 2nd particle toward the first particle and etc. The correction would be minuscule because it would happen every frame so a large deviation could never accumulate. Is there any way to set this up? (Possibly does the api allow direct manipulation of particle positions after the iterative solver runs?)
This algorithm you describe is called FTL (follow the leader):http://matthias-mueller-fischer.ch/publications/FTLHairFur.pdf. A similar idea is the basis for the FABRIK IK solver.
Unfortunately, its raw version which is the one you described doesn't work at all for dynamic simulation. The issue with this approach is that every constraint behaves as if the previous particle has infinite mass (as it is only allowed to move the next one). As a result, the rope behaves in a extremely weird way, similar to a whip. The only solution is to modify particle velocities in a non-physical way, which introduces a lot of damping (dynamic follow the leader or DFTL). This method is generally used for hair simulation, as it is very cheap and hair itself usually has a lot of damping due to air drag and collisions with neighboring hairs, so the weird behavior and high damping are not a big problem. Tried it for ropes, and believe me it does not look like a rope.
I also experimented with a direct tridiagonal matrix solver (the Thomas algorithm), and it was even included in Obi for a short time (versions 2.0-2.3 I believe) with the name of "Chain constraints". It does yield a perfectly stiff rope in a single iteration and unlike DFTL it supported fixing the rope at arbitrary points (not just one end). However it becomes extremely unstable when there's no possible solution to the equation system (e.g, an overstretched rope) due to the linearization of the problem. In the end it caused more pain than it was worth, so we just dropped it from Obi. Distance constraints at low timesteps work much better, take less time to calculate, and are unconditionally stable.
(04-02-2018, 08:03 PM)jonworks Wrote: It's probably a little hacky and I wouldn't suggest it except that I'm really floundering trying to get this to work. When I apply tethers for each particle linked to the fixed particle the rope always wants to fold into a squiggly line and rotate around never settling down. And when I pull the rope (use cursor to reduce the length) it becomes extremely jerky like tugging the rope every few frames instead of pulling it smoothly. I've tried other tether strategies like changing the stiffness, only tethering every nth particle to the fixed particle, and even chaining them together (like tethering the 32nd particle to the 24th, the 28th to the 20th and etc). Nothing seems to give me anything other than a rubber band or a squiggly line that won't shorten with any degree of stability.
May I ask, what exactly is your end goal? tethers have proven to behave in a more than acceptable way for most cases (pendulums, cranes, etc), and reducing the timestep/increasing iterations with just distance constraints works also really good for most uses. Unless you need very long ropes (+10 meters) with very accurate collision detection and absolutely zero compliance, It shouldn't be difficult at all to get quite realistic rope simulation going on. At this point I'm a bit baffled that none of the proposed solutions worked well enough for you. Maybe I can offer better help if I understand what you're after.
cheers!
Posts: 11
Threads: 2
Joined: Jan 2018
Reputation:
0
Thanks for your patience with my problems. My game is a building game and players can use the rope in a variety of ways (including ways I haven't thought of). It's not critical that the rope behaves identically under all circumstances. However it is critical that the rope behaves remotely like a rope under all circumstances and doesn't spaz out or crash the game.
The rope must be able to support a 1kg weight at 1m long, then be extended to 60m long and pick up/support a 1000kg weight, then lift the 1000kg weight up to the starting point.
This sounds like a tall order, but it really shouldn't be, because the rope parameters can be adjusted since the rope does not need to behave uniformly. IE, if the rope can be made to work at both of those extremes, then it can be made to work anywhere in between by adjusting the parameters in real time. For example a large part of my previous solution with rope that uses the built-in physics engine was adjusting the resolution (length of capsule colliders in that case) and the rope mass, depending on the length and attached mass respectively. My previous rope worked fine within those parameters, but as I've mentioned before, just crashed the game a certain percentage of the time.
Currently the case I'm unable to resolve is a 40m rope with a 400kg mass attached. Without tethers (regardless of iteration count or resolution) it's too much like a rubber band. With tethers the rope is always a squiggly line and behaves like it's yanking the rope every few frames instead of exerting uniform pulling force on the mass.
I tried starting with your crane example to give you some steps to reproduce, but like everything in this project it has been an odyssey of frustration. The rope won't extend past a certain distance. Looked in the crane controller and I can't find a limiter there. Tried increasing pooled particles using cursor.rope.pooledParticles both in Start() and Update() but no change. Tried increasing pooled particles for the rope in inspector while playing but no change. Tried increasing the pooled particles before pressing play but no change. Tried initializing the rope after increasing the pooled particles both while playing and before playing but it broke the whole thing and now the rope just falls into the void.
Thanks,
Jon
Posts: 6,321
Threads: 24
Joined: Jun 2017
Reputation:
400
Obi Owner:
05-02-2018, 09:33 AM
(This post was last modified: 05-02-2018, 11:45 AM by josemendez.)
(05-02-2018, 12:26 AM)jonworks Wrote: Thanks for your patience with my problems. My game is a building game and players can use the rope in a variety of ways (including ways I haven't thought of). It's not critical that the rope behaves identically under all circumstances. However it is critical that the rope behaves remotely like a rope under all circumstances and doesn't spaz out or crash the game.
The rope must be able to support a 1kg weight at 1m long, then be extended to 60m long and pick up/support a 1000kg weight, then lift the 1000kg weight up to the starting point.
This sounds like a tall order, but it really shouldn't be, because the rope parameters can be adjusted since the rope does not need to behave uniformly. IE, if the rope can be made to work at both of those extremes, then it can be made to work anywhere in between by adjusting the parameters in real time. For example a large part of my previous solution with rope that uses the built-in physics engine was adjusting the resolution (length of capsule colliders in that case) and the rope mass, depending on the length and attached mass respectively. My previous rope worked fine within those parameters, but as I've mentioned before, just crashed the game a certain percentage of the time.
Currently the case I'm unable to resolve is a 40m rope with a 400kg mass attached. Without tethers (regardless of iteration count or resolution) it's too much like a rubber band. With tethers the rope is always a squiggly line and behaves like it's yanking the rope every few frames instead of exerting uniform pulling force on the mass.
I tried starting with your crane example to give you some steps to reproduce, but like everything in this project it has been an odyssey of frustration. The rope won't extend past a certain distance. Looked in the crane controller and I can't find a limiter there. Tried increasing pooled particles using cursor.rope.pooledParticles both in Start() and Update() but no change. Tried increasing pooled particles for the rope in inspector while playing but no change. Tried increasing the pooled particles before pressing play but no change. Tried initializing the rope after increasing the pooled particles both while playing and before playing but it broke the whole thing and now the rope just falls into the void.
Thanks,
Jon
Hi Jon,
Now that I understand the use case I can tell you with confidence that Obi is not the right tool for this. There are very few (if any) realtime solvers capable of doing what you ask for, as it is a textbook worst-case scenario for physics simulations: very large chain of constraints coupled with a huge (1:10000, assuming 0.1kg per rope node) mass ratio. If you also want to support arbitrary collisions with the rope, things get even more difficult. Adjusting simulation parameters according to the situation is not even your biggest problem, but getting an accurate simulation of a 60 meter rope with 1 ton weight at its end is borderline impossible.
Try reducing the physics timestep A LOT (0.001 or 0.0005) while using only vanilla distance constraints and 10 iterations, see if that somewhat improves the results. Be warned however that this will reduce performance, so I'm not that optimistic.
To my knowledge, the only existing algorithm capable of withstanding this would be ABA (articulated bodies). It is way more expensive than more traditional approaches, but it will be able to handle your scenario. It comes with its own set of limitations tough: rope cannot form closed loops (you're limited to straight segments, which could or could not be of importance to you) and contact handling is very slow (linear in the amount of segments for a single contact, so quadratic worst case complexity compared to Obi's constant/ worst case linear complexity). Also, I don't know of a good implementation of this in Unity. Newer PhysX versions include ABA (see https://www.youtube.com/watch?v=80cMpjdwPAM), but it is not integrated into Unity yet. Will let you know if I come across any good tool for this.
Posts: 11
Threads: 2
Joined: Jan 2018
Reputation:
0
Well, OK, I suppose I can give up at this point and stick with the rope solution I had before.
I'd read about this problem as it relates to Obi before, but I figured it could be bypassed because mass ratio is always mentioned in the same sentence. I do not require the rope to have low mass. When you crank up the weight of the rope it starts to behave more like a heavy cable, which is exactly what you'd expect when lifting something really heavy. Adjusting the mass of the rope to keep the mass ratio within the simulation limit is how I got my current rope solution to work. But I guess it doesn't work with Obi.
Thanks,
Jon
Posts: 6,321
Threads: 24
Joined: Jun 2017
Reputation:
400
Obi Owner:
06-02-2018, 01:02 PM
(This post was last modified: 06-02-2018, 01:12 PM by josemendez.)
(06-02-2018, 03:20 AM)jonworks Wrote: Well, OK, I suppose I can give up at this point and stick with the rope solution I had before.
I'd read about this problem as it relates to Obi before, but I figured it could be bypassed because mass ratio is always mentioned in the same sentence. I do not require the rope to have low mass. When you crank up the weight of the rope it starts to behave more like a heavy cable, which is exactly what you'd expect when lifting something really heavy. Adjusting the mass of the rope to keep the mass ratio within the simulation limit is how I got my current rope solution to work. But I guess it doesn't work with Obi.
Thanks,
Jon
Hi Jon,
Obi has exactly the same limitations as PhysX when it comes to mass ratios, since they are both iterative solvers and use integrators of the same order. For the same mass ratio / constraint chain length / iteration count / step length combination, both behave the same way. The difference is that PhysX (Unity's engine) becomes unstable (jittering, velocity spikes and the possibility of a crash) when the rope is overstretched, and Obi does not (things just stretch).
Btw, cranking up the weight of the rope yields exactly the same dynamics (same visual behavior) as lowering the mass of the load. So why do you need the load to be so heavy, if you can afford the rope to be very heavy too? Having a low mass ratio (similar rope weight and load weight), will make the load look light because it will not tense the rope quick enough.
That being said, I still believe that using an iterative solver for this is a bad idea. Found some AgXDynamics bindings for Unity, it's an engineering grade simulator that features both direct and iterative solvers. You might want to give it a run: https://github.com/Algoryx/AGXUnity
|