Hi, is Tokamak unsuitable for non-fixed timesteps? I'm using a delta time system (passing in the number of seconds that have elapsed since the last frame, in a single precision float, to neSimulator::Advance), and I'm noticing that on faster framerate machines I have problems:
At 60 fps (vsync locked), the game seems to control properly.
At 1000+ fps (unlocked), the same rigid body will come to rest on the animated body (small rebound impulse), but an assertion is thrown in neQ::Normalize that the quaternion has become entirely NaN. If I run in Release mode, the NaN quaternion causes my camera projection to break, which seems to indicate to me that it's the rigid body's rotation.
I notice that all of the sample code uses 1/60 for its timestep, and if I change my simulator timestep to 1/60, the behaviour acts the same as on 60fps (obviously).
I've never done a fixed timestep system before; I could do it but it seems to me that Tokamak should be able to handle very small timesteps accurately. The object isn't moving terribly fast, and the sensors seem to pick up the collisions properly.
Is there something I'm missing here that could be causing this behaviour? I hope that I'm just seeing this as a red herring, and the simulator instability is from some misconfiguration in neSimulator or my animated/rigid bodies.
Thanks in advance
A clarification: It doesn't matter if it's 1/60, 1/80, 1/600 or 1/2000; as long as the timestep is fixed it seems to work fine. I suspect this might still be a red herring.
I have run into something similar when attempting to pass a timestep of 0 to the simulator.
I'm looking into this right now.
If you have verified that the step is actually > 0.0
Incidentally, in my own sandbox code I have a check around Advance along the lines of to prevent what you might be describing:
I'm pretty sure the behaviour is a philosophical choice, because a timestep of zero raises some ambiguities, like whether callbacks and sensors should be processed if the timestep is 0, and also, being able to assume a > 0 step removes a bunch of special casing in the simulator.
dang I hit post too quickly:
as i was saying, in the case of timestep = 0.0, the simulator will die. I believe this is the correct behaviour since a timestep of zero means no simulation, and should be tested before calling advance.
Can you verify that your timestep really is > 0.0f and let me know?
I will look into it further, if you can isolate a small number that makes the simulator die...
It does seem that this is the problem, actually. Sorry to waste your time; thanks for the help.
Also, can you clarify what the value for SetSleepingParameter is supposed to be? Does 0.0f indicate that the object sleeps immediately, or never? What about 10.0f? It's not mentioned in the documentation at all.
No prob, glad to help.
SetSleepParam ( 0.0f ) should make the object stay active always.
Im not sure what values > 1 do, but im guessing the higher the value, the more energy the engine will discard in order to put something to sleep.
i.e. a projectile with a sleep factor of 100.0 might just fall asleep in mid air.
Also wanted to say, feel free to ask about any further problems... this stuff ain't easy, and pretty much everything you've asked is stuff that I've also encountered, so it's nice to be able to reuse the knowledge!
Excellent -- I do have one other problem, but it's a long shot.
On my Mac, I went and built the Tokamak source code as a static library. Running on the same game code (albeit locked at 72-75 fps instead of 1000+) I seem to be getting significantly bigger "bounces" when impacting the animated body. It's still playable, but the simulation seems a lot less smooth.
I'm wondering if this is because the source release is newer than the last Tokamak Win32 binary, or if there's somehow something different about how GCC works that I should be aware of.
I should probably build the samples to confirm.
Have you tried increasing the number of substeps as a function of your timestep?
If you are currently passing something like: Advance(simStep, 1, &report),
try passing 2 or 3 instead of 1, and see if this smoothes things out at the cost of a more CPU intensive advance, and your callbacks being called more often...
It smooths it out a little; I'll have to experiment with it a bit. I think I might be getting a timing hitch on this platform; from an equal drop with equal gravity, sometimes it rebounds with v = 0.3 and on some other launches v = 0.5 from an impact of v = 0.9. It's not substantial.
Is it alright to use the rigid body controller callback to only cache the sensor positions, etc, and then have an update function in the game code which later applies things like the spring damping force (based on those cached positions) as impulses?
Basically, is it okay to do everything all at once, or should it be done sensor-by-sensor in the callback, like in the example car source?
I'm nearly positive that its best to do all your force application and sensor processing ONLY in the callbacks.
You can get away with applying forces outside the simulator, but then the behaviour will vary based on the framerate of the target.
If you do all your force application in the callbacks, they will happen consistently, and you can control the interval and rate by only changing your Advance parameters.
All your other parameters and numeric forces can stay constants.
There are definite problems to solve in terms of when you read your user control inputs however.
I reccomend that you sample user inputs separately and on a separate interval, like perhaps 10x per second, and then refer to those stored values in your callbacks. This should then remain relatively consistent as long as your framerate doesn't drop much below 10fps. (This is off the top of my head, so ymmv;)
Offhand, I doubt that you would see much, if any, difference in numeric stability from using gcc vs msvc.
A little clarification pseudocode is in order:
const float minimumStep = 0.05
float lastSimUpdateTime = 0.0f;
float currentSimTime = 0.0f;
Lets say 0.1 seconds have elapsed since last frame so:
float gameDeltaT = 0.1;
currentSimTime += gameDeltaT;
float simDeltaT = currentSimTime-lastSimUpdateTime;
int numSubSteps = (int) (simDeltaT / minimumStep);
Advance( minimumStep * numSubSteps, numSubSteps, &report);
lastSimUpdateTime += (minimumStep*numSubSteps);
Let me know if this seems problematic... and/or helps your problem.
Ah, that helps a lot more! That makes the substeps parameter make a lot more sense, and now the game runs pretty much identically on both platforms. Thanks a lot; you've been very helpful with all of my questions so far.
I've been trying to update the tokamak.h file with some Doxygen documentation as I go; I've found another place that did it (http://assemblerbot.info/tokamak/src/html/annotated.html) but theirs is fairly incomplete. When I'm done, I'll try to post it on my website. Do you have any similar code-level documentation? We could send it in to the author of the library.