This is the system I finally developed :
Fetch the time elapsed between frames.
Accumlate this time.
If the time is over 20 ms (50 Hz), perform physics and capture the scene. Reset the frame timer.
For each frame, calculate a tween value.
Render with Tween.
Here's the code :
Here's an example of doctoring the JV-ODE Box Demo :
The 50 Hz is hard coded as the value 20 in the above. You could use a CONST and set that to something else. 50 Hz was used as it divides nicely into the limited 1000 ms resolution of the timer. 60 Hz doesn't, and I think it'll give rounding errors. Using a period less than 1/60th of a frame will have no effect if you lock to the vertical refresh. If you lose the vertical lock (Flip False) you could get a higher framerate than the screen update, but at a cost of introducing tearing.
This method has been tested to provide the same physics update rate at 60 Hz and 72 Hz, including custom forces (just apply all forces in the 'If update_time' section Update) where my previous time-based Step-size didn't.
Fetch the time elapsed between frames.
Accumlate this time.
If the time is over 20 ms (50 Hz), perform physics and capture the scene. Reset the frame timer.
For each frame, calculate a tween value.
Render with Tween.
Here's the code :
CaptureWorld() start_time=MilliSecs() ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Get frame start time While Not KeyHit(1) frame_time=MilliSecs()-start_time ;<<<<<<<<<<<<<<<<<<<<< Get time for frame start_time=MilliSecs() ;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Reset frame start update_time=update_time+frame_time ;<<<<<<<<<<<<<<<< Increase count for time between updates If update_time/20>0 ;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< If updating 50 fps update_time=update_time Mod 20 ;<<<<<<<<<<<<<<<<<< Return remainder CaptureWorld() ;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Record object positions ;<DO OBJECT MOTION, PHYSICS, ETC.> EndIf tween#=(update_time Mod 20)/20.0 ;<<<<<<<<<<<<<<<<< Get render tween amount UpdateWorld RenderWorld tween# Flip Wend
Here's an example of doctoring the JV-ODE Box Demo :
; ################################################################################################### ; # JV-ODE - Cubes Demo # ; # Code by Jim Williams (VIP3R) # ; ################################################################################################### AppTitle "JV-ODE - Cubes Demo" Include "JV-ODE.bb" Graphics3D 800,600,0,2 Type ODEGeom Field body Field geom Field mesh End Type ; ################################################################################################### ; ### Setup ODE Global World=dWorldCreate() Global Space=dHashSpaceCreate(0) Global ContactGroup=dJointGroupCreate(0) dWorldSetAutoDisableFlag(World,1) dWorldSetGravity(World,0,-0.98,0) dContactSetMode(dContactBounce) dContactSetBounce(0.1) dContactSetMu(48) ; ### Create light Global Light=CreateLight() RotateEntity Light,45,-90,0 LightColor Light,255,255,255 AmbientLight 130,130,130 ; ### Create camera pivot=CreatePivot() Global Camera=CreateCamera(pivot) CameraClsColor Camera,0,0,0 CameraRange Camera,1,1000 PositionEntity Camera,0,20,-50 RotateEntity Camera,30,0,0 ; ### Create plane dCreatePlane(Space,0,1,0,0) Plane=CreatePlane() EntityAlpha Plane,0.8 PlaneTexture=CreateTexture(128,128,9) ClsColor 0,200,80 Cls Color 255,255,255 Rect 0,0,64,64,1 Rect 64,64,64,64,1 CopyRect 0,0,128,128,0,0,BackBuffer(),TextureBuffer(PlaneTexture) ScaleTexture PlaneTexture,20,20 EntityTexture Plane,PlaneTexture,0,0 Mirror=CreateMirror() ; ################################################################################################### CaptureWorld() start_time=MilliSecs() ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Get frame start time While Not KeyHit(1) frame_time=MilliSecs()-start_time ;<<<<<<<<<<<<<<<<<<<<< Get time for frame start_time=MilliSecs() ;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Reset frame start update_time=update_time+frame_time ;<<<<<<<<<<<<<<<< Increase count for time between updates If update_time/20>0 ;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< If updating 50 fps update_time=update_time Mod 20 ;<<<<<<<<<<<<<<<<<< Return remainder CaptureWorld() ;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Record object positions UpdateGeoms() dSpaceCollide(Space,World,ContactGroup) dWorldQuickStep(World,0.15) dJointGroupEmpty(ContactGroup) TurnEntity pivot,0.0,0.2,0.0 EndIf tween#=(update_time Mod 20)/20.0 ;<<<<<<<<<<<<<<<<< Get render tween amount If MilliSecs()-Timer>300 AddObject() UpdateGeoms() box_count=box_count+1 Timer=MilliSecs() End If UpdateWorld RenderWorld tween# Text 5,5,"boxes : "+box_count Flip False Wend dJointGroupDestroy(ContactGroup) dSpaceDestroy(Space) dWorldDestroy(World) dCloseODE() End ; ################################################################################################### Function AddObject() xp=Rand(-10,10) zp=Rand(-10,10) xs=Rnd(3,6) ys=Rnd(3,6) zs=Rnd(3,6) ode.ODEGeom=New ODEGeom ode\body=dBodyCreate(World) dBodySetRotation(ode\body,0,0,0) dBodySetPosition(ode\body,xp,30,zp) dBodySetAutoDisableFlag(ode\body,1) ode\geom=dCreateBox(Space,xs,ys,zs) dGeomSetBody(ode\geom,ode\body) ode\mesh=CreateCube() ScaleMesh ode\mesh,xs/2.0,ys/2.0,zs/2.0 EntityColor ode\mesh,Rand(255),Rand(255),Rand(255) EntityAlpha ode\mesh,1 EntityShininess ode\mesh,0.7 End Function ; ################################################################################################### Function UpdateGeoms() For ode.ODEGeom=Each ODEGeom If dBodyIsEnabled(ode\body) RotateEntity ode\mesh,dGeomGetPitch#(ode\geom),dGeomGetYaw#(ode\geom),dGeomGetRoll#(ode\geom) PositionEntity ode\mesh,dGeomGetPositionX#(ode\geom),dGeomGetPositionY#(ode\geom),dGeomGetPositionZ#(ode\geom) End If Next End Function ; ###################################################################################################
The 50 Hz is hard coded as the value 20 in the above. You could use a CONST and set that to something else. 50 Hz was used as it divides nicely into the limited 1000 ms resolution of the timer. 60 Hz doesn't, and I think it'll give rounding errors. Using a period less than 1/60th of a frame will have no effect if you lock to the vertical refresh. If you lose the vertical lock (Flip False) you could get a higher framerate than the screen update, but at a cost of introducing tearing.
This method has been tested to provide the same physics update rate at 60 Hz and 72 Hz, including custom forces (just apply all forces in the 'If update_time' section Update) where my previous time-based Step-size didn't.