Errol_Summerlin wrote:--and I'm having a wild idea about that, that maybe rk4step() could compute rotation first, update the interpolation structure, and pass a pointer to it as an argument to forces, torques and mass loss...
One of us is not understanding. In rk4, to get, for example, the position at the intermediate state, I compute intermediate_position=x0+v0*dt..an euler step. It is just 1 multiplication and 1 addition.
First off, Errol, I want to apologize for this discussion taking so long; so many long posts. One of us is not understanding, indeed; and I'd bet it's me, given your much greater knowledge of physics and math; but then again my argument seems so clear to me I can't help another part of me believing the opposite. So, let's say I'm 50-50 on my bets; or maybe 75-75, as there's a high chance we're both not understanding --at least each other. But I'm sure you
want me to understand, rather than drop the subject; and I want to understand, too.
I already got the idea that those "funny half steps" and whatnot in rk4 are Euler steps, and that they are computationally cheap as far as their Euler-ness is concerned. So, possibly our problem is semantic. I'm looking at the amount of code in helper(), which is called 3 times from rk4step() (lines 198, 199 and 200), and which in turn calls getForceTorqueandMassLoss() (line 132). Besides this call, helper() contains a total of 16 lines of computational code; --heavy code, where variables are as likely to be vectors as scalars. Not complaining; I just wanted to say that when I talk about "half steps" I mean "funny computations that take time", whereas maybe you have a more narrow definition of the term, involving "just one multiplication and one addition". There's also another call of getForceTorqueandMassLoss() from rk4Step() itself (line 233), and some more heavy code preceding it.
That's what I'm looking at, in general. I never meant to imply that the half-steps were anything other than Euler steps.
What I meant to say is that when you want a force vector from me, 4 times per rk4step (one in rk4 step, plus once per helper() call, times three calls) you expect vectors that are true for those positions. No? I take your current place-holder getForce...() as example, where the force line reads:
Code: Select all
FTML.Force = -1.0*state.mass*state.position/pow( position.length(), 3.0 ); //gravity like force pulling object toward (0,0,0)
and where state.position is the position AT the half-step or whatever position you call the function from, right?
This gives you a "gravity" vector true from that position.
But now, if the unit is rotating AND thrusting, presumably you'd similarly want true thrust vectors (or something as close as possible to true thrust vectors) AT such positions and times. No?
If so, to give you true (or close to true) thrust vectors, I need to be able to interpolate or extrapolate or somehow estimate my ship's orientation at those locations and times.
Unless there is something VERY fundamental I'm missing here. Am I getting the general idea right, about rk4? What it seems to me it's doing is sort of "super-sampling" the physics step, like testing what it gets from a full Euler step, versus what it gets from two consecutive Euler half-steps and applying some magical theorem to combine these results in the right ratios to get a much more accurate estimate, right? And these "super-samplings" happen at "future times", like by 1 and 0.5 dt's and whatnot, right? And so at each of those samplings you need a force vector as would be true at that position and time?
If so, to give you a true vector I need to estimate how much my ship has rotated during those 1 dt and 0.5 dt intervals.
And to do such an estimation I need either times and rotational speeds, to do my own Euler on my ship's orientation; or else times and two orientations (e.g. quaternions) 1 dt apart, to interpolate between.
And I was saying that the change in orientation ***for a ship, with thrusters*** is probably very small over a dt; so I could do a cheap, quaternion interpolation, for example; but you're the math wiz.
(((NOTE: I *almost* wrote that all I might need are two forward vectors to interpolate between, because it's "just a thrusting vector" I need to compute; but then I realized I could be thrusting down, or to the left, say; rather than thrusting forward; so no: I would need a full quaternion of orientation.)))
The other, COMPLETELY SEPARATE issue, is rotation simulation, which is what you seem to be discussing below...
To interpolate the position at the intermediate state linearly given x1 and x0, you compute intermediate_position=x0+(x1-x0)*dt/deltaT. This is also just 1 multiplication and 1 addition if you pre-compute (x1-x0)/deltaT.
This is the same for rotations. The linear interpolation is just as expensive computationally as the calculation for the intermediate state in rk4(which remember, is just an euler step). Computing the intermediate position via interpolation does not save you any time computationally.
I never meant to imply it would; and I never suggested interpolation for the intermediate position for rotation simulation. I suggested it for computing thrusting vector, in Unit code; a completely separate issue.
Once the intermediate state is calculated, rk4 asks the system to tell it what the force, torque and mass loss are at the intermediate (time,position, orientation, mass, and any other state variables that may be relevant to those quantities.
Exactly. And I only mentioned interpolation of rotations in connection to computing force, in Unit code; NOT in rk4step.
To reiterate, an euler step and a linear interpolation are the same computational cost in terms of computing the intermediate state in rk4.
I never doubted it. Interpolation is definitely even a little more expensive than a Euler step.
The reason for using interpolation is to avoid calling getForceTorqueandMassLoss for every frame...not to avoid computing the position at every frame.
It never even crossed my mind to use interpolations for position anywhere in rk4.
That always has to be done anyway. With the proposed interpolation of rotations, you are still calling getForceTorqueandMassLoss just as often, you are using just as much computation to calculate the intermediate states via interpolation as you would to calculate the, and you are still performing just as many quaternion multiplications (regardless of whether you use an euler step or an interpolation, you still have to do the rotation business)
Again, I never meant to suggest any kind of interpolations as a substitute for calling getForceTorqueandMassLoss(). What I was suggesting is that IN the getForceTorqueandMassLoss() function (which has yet to be written), I need to somehow interpolate orientations, so as to be able to give you true force (thrust) vectors; but that unless you give me some interpolation data, I cannot perform such interpolations.
So, for the sake of an intellectual exercise: rk4step code could shift a bit, such that the code lines pertaining to rotation could go first, at the top of the function, and come up with a "next" quaternion of orientation. Then, when you call getForce....() you give me the previous and next quaternions, and a t; and so I can do a quaternion interpolation at time t, and then compute my thrusting vector based on that, and return. Then you got a PrettyGood(TM)_force_vector from me. I'm trying to be nice
But now you could ask "How are we going to compute rotation early in the rk4step() function, when we need to call helper() first and all that?"
If that's your question, my answer continues to be that I would remove all code related to rotation from helper(), and do just a simple Euler simulation of rotation early in rk4, and leave it at that. Why? Because I don't think accuracy of rotation simulation is important at all; but that the accuracy of the thrusting direction consequences of (however inaccurate) rotations are much more important.
Since the intermediate values are already being calculated the simplest way they can(an euler step), the only way to actually save on computation time is to just not calculate the intermediate value for some variables whose variation over the interval is deemed unimportant to the getForceTorqueandMassLoss function.
I'd like to change your sentence to "Since the intermediate values are already being calculated the simplest way they can(an euler step), the only way to actually save on computation time is to just not calculate the intermediate value for some variables whose variation over the interval is deemed unimportant." (Period.)
Rotation is unimportant. (AND expensive.)
In fact, rotation is important IN getForce...(), as far as thrusting direction agreeing with rotation; but not in absolute terms.
One could suggest that, with sufficiently small time steps mass variation over an interval could be small enough to be negligible. One could also argue that holding orientation constant over the interval would produce errors that are not perceived by the player. The latter of these avoid 4 out of 5 quaternion rotations by not computing intermediate orientations at all.
Not sure what you mean by "holding orientation constant over the interval". What I've been suggesting, but I don't think I ever did clearly enough, is that at the top of rk4 we could do a simple Euler step for orientation:
Code: Select all
next_rotational_speed = last_rotational_speed + rotational_accel*dt;
next_orientation_quat = last_orientation_quat * quat(0.5*(last_rotational_speed+next_rotational_speed)*dt);
DONE!
No half steps. No helper() code for rotation.
THEN, when you call my getForce() function, you give me those quats:
Force = getForce( position, etceteras,
last_orientation_quat, next_orientation_quat, t );
so that I can interpolate between those orientations and give you properly oriented force vectors.
Okay, if I'm understanding correctly, you had no plan to obtain interpolated vectors from me, when you call my forces, torques and mass loss function. Correct? I always assumed I was expected to come up with them, somehow.
Correct, the values used to call getForceTorqueandMassLoss are the exact values of the state for which rk4 needs those quantities.
Occasionally we DO understand each other
so there's light at the end of the tunnel...
I think we should pass rotation and rotation interpolation data to the callee, so that I can give you accurate force vectors (for your rk4 translational computations).
As I said before, this just costs accuracy and doesn't actually save any computation.
And here understanding drops to zero, again
I was suggesting that precisely to INCREASE accuracy; NOT to save computations.
If you interpolate in getForceTorqueandMassLoss or I do an euler step in the rk4helper, the computational cost is the same. However, rk4 EXPECTS euler step results. The weights for each of the intermediate state derivative values are based on the assumption of an euler step in computing the intermediate states. If I were to use a midpoint step instead of an euler step, I may need different weights for the different interpolated derivatives.
Here I'm lost; but in case I'm understanding something, I never suggested changing ANYTHING about the way you compute RK4.
I was simply saying, and I don't know how to say it more clearly, that the code INSIDE getForce...() needs to interpolate orientation, to be able to give you back thrusting forces that reflect the fact that the ship has changed orientations at each call, as they are spread in time by multiples of 0.5*dt.
This I don't get. A planet has no torque forces acting on it, or thrusters; therefore doing Euler or RK4 for rotation, in my mind, would give us the exact same result, no?
That was a bad example.
Okay, but do give me a good one, then. I can't fathom any.
My point was that interpolations are where lots and lots of quaternion rotations are, and eliminating 4 from the physics step doesn't really save you much. It saves you especially little for planets. And it damage gameplay and believability for players.
Ditto; I need a good example of this damage.
And it's not clear to me that we need such heavy code in the interpolations as we have now in rk4step() and helper(). I'm hopeful we can optimize the code to avoid conversions back and forth between vectors of rotation, angle and axis, and quaternion representations.
You claim you are proposing an "euler" step for rotation, but I have no idea what you mean by that.
In pseudo-code:
Code: Select all
void RigidBody::rk4Step( double dt ) //non-reentrant; see comment about static variables
{
...............................................
next_rotational_speed =
last_rotational_speed + rotational_accel*dt; // <<<<<<<<<<<<<<<<< NEW
next_orientation_quat =
last_orientation_quat * quat(0.5*(last_rotational_speed+next_rotational_speed)*dt); // <<<<<<<<< NEW
...............................................
helper( *this, 0.5*dt, d2, last_orientation_quat, next_orientation_quat ); // <<<< ADDED PARAMS
helper( d2, 0.5*dt, d3, last_orientation_quat, next_orientation_quat ); // <<<< ADDED PARAMS
helper( d3, dt, d4, last_orientation_quat, next_orientation_quat ); // <<<< ADDED PARAMS
...............................................
}
Then helper() would pass these two quaternions to getForce...() so that I can interpolate orientation at t.
But helper() would NOT do any computations related to rotation and orientation. Orientation is done, complete, finito, by the time helper() is called for a first time.
I have tried to articulate why that is a misnomer and why the pseudocode you presented (involving interpolating the intermediate states) doesn't actually save you any computation (in my previous post and again in this one). I have also tried to explain the options for saving computation by reducing the number of quaternion rotations, but that is NOT an euler step for rotations.
But I think you've all along been mixing up ***where*** I suggest to interpolate, and ***where*** to use Euler.
My interpolation suggestion has always been for getForceTorqueandMassLoss(), --i.e. Unit code; NOT for rk4 code.
And my Euler suggestion was simply to simplify rotational simulation, --since accuracy for it is irrelevant--, to a single Euler step.
The only way to get rid of most of the quaternion rotations in the physics step (what I THINK you are concerned about, though at this point I am not so sure) is to not compute them.
EXACTLY!
In each rk4helper call, just return the orientation as it came in
Better than that: Don't touch it. Don't even look at it.
and use that constant orientation to compute getForceTorqueandMassLoss.
NO! This is what I'm trying to say IS important in terms of accuracy. getForceTorqueandMassLoss() does need to use orientation interpolations to correctly orient thrust. But these interpolations should be much lighter than the ones you currently do in helper(). Interpolating between two quaternions that differ little boils down to a linear interpolation of the terms and renormalizing.
Then, at the end, you compute orientation the same way it is currently computed, as a weighted average of the angular_velocities at the intermediate states. This only involves one quaternion multiplication per physics step as opposed to 5. However, I should point out that this won't help with a more cumbersome problem...that of moment of inertia matrix operations.
Hmmm...
How about we 'cross that bridge when we get there'? I've no idea about it; but at least I'm sure we are only going to do it once per physics step; NOT four times...
EDIT:
And if we still don't understand each other, I will ask Klauss to enlighten us; he usually understands all sides in any argument; but has been notably silent in this one.
EDIT2:
Update: Class bit_array is now updated to be configurable to any number of bits (but fixed at compile time), and it compiles.